Compare commits

..

11 Commits

47 changed files with 1099 additions and 17 deletions

View File

@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"Bash(dir:*)"
],
"deny": [],
"ask": []
}
}

View File

@ -2,7 +2,7 @@
"BuildId": "37670630",
"Modules":
{
"ProjectFish": "UnrealEditor-ProjectFish.dll",
"ProjectFishEditor": "UnrealEditor-ProjectFishEditor.dll"
"ProjectFish": "UnrealEditor-ProjectFish-0001.dll",
"ProjectFishEditor": "UnrealEditor-ProjectFishEditor-0001.dll"
}
}

View File

@ -13,6 +13,7 @@ FixedCameraDistance=1500.0
+PrimaryAssetTypesToScan=(PrimaryAssetType="PrimaryAssetLabel",AssetBaseClass="/Script/Engine.PrimaryAssetLabel",bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown))
+PrimaryAssetTypesToScan=(PrimaryAssetType="FishingRewardDataAsset",AssetBaseClass="/Script/ProjectFish.FishingRewardDataAsset",bHasBlueprintClasses=False,bIsEditorOnly=False,Directories=((Path="/Game/DataAssets/FishReward")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=AlwaysCook))
+PrimaryAssetTypesToScan=(PrimaryAssetType="ShapeAsset",AssetBaseClass="/Script/ProjectFish.ShapeAsset",bHasBlueprintClasses=False,bIsEditorOnly=False,Directories=((Path="/Game/DataAssets")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=AlwaysCook))
+PrimaryAssetTypesToScan=(PrimaryAssetType="QuestAsset",AssetBaseClass="/Script/ProjectFish.QuestAsset",bHasBlueprintClasses=False,bIsEditorOnly=False,Directories=((Path="/Game/DataAssets/Quest")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=AlwaysCook))
bOnlyCookProductionAssets=False
bShouldManagerDetermineTypeAndName=False
bShouldGuessTypeAndNameInEditor=True

View File

@ -10,5 +10,8 @@ InvalidTagCharacters="\"\',"
+GameplayTagRedirects=(OldTagName="Skill.PoserPull",NewTagName="Skill.PowerPull")
NumBitsForContainerSize=6
NetIndexFirstBitSegment=16
+GameplayTagList=(Tag="FishReward",DevComment="鱼获")
+GameplayTagList=(Tag="FishReward.Fish1",DevComment="")
+GameplayTagList=(Tag="FishReward.Fish2",DevComment="")
+GameplayTagList=(Tag="Skill.PowerPull",DevComment="")

Binary file not shown.

Binary file not shown.

View File

@ -2,6 +2,6 @@
"BuildId": "37670630",
"Modules":
{
"DeskMode": "UnrealEditor-DeskMode.dll"
"DeskMode": "UnrealEditor-DeskMode-0001.dll"
}
}

View File

@ -6,6 +6,7 @@
#include "ContainerInfo.h"
#include "GameFramework/SaveGame.h"
#include "ProjectFish/Definations.h"
#include "ProjectFish/Quest/QuestTypes.h"
#include "PlayerInfoSaveGame.generated.h"
/**
@ -43,4 +44,8 @@ public:
// 玩家仓库物品存档数据
UPROPERTY(SaveGame, BlueprintReadWrite, meta = (ToolTip = "玩家仓库物品数据"))
TArray<FContainerItemSaveData> PlayerContainerItems;
// 任务存档数据
UPROPERTY(SaveGame, BlueprintReadWrite, meta = (ToolTip = "任务存档数据"))
TArray<FQuestSaveData> QuestSaveData;
};

View File

@ -5,6 +5,7 @@
#include "CoreMinimal.h"
#include "ShapeAsset.h"
#include "Engine/DataAsset.h"
#include "ProjectFish/Quest/QuestTypes.h"
#include "FishingRewardDataAsset.generated.h"
UENUM(BlueprintType)
@ -36,4 +37,7 @@ public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Fishing Reward Data" , meta = (ToolTip = "鱼获稀有度"))
ERewardRarityType RewardRarityType;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Fishing Reward Data" , meta = (ToolTip = "鱼获对应的任务信息"))
FQuestTargetInfo QuestTargetInfo;
};

View File

@ -0,0 +1,88 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "QuestAsset.h"
#if WITH_EDITOR
#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetRegistry/AssetData.h"
#include "Misc/MessageDialog.h"
void UQuestAsset::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
// 检查是否修改了 QuestID 属性
if (PropertyChangedEvent.Property != nullptr &&
PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UQuestAsset, QuestID))
{
// 获取资产注册表模块
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
// 查找所有 QuestAsset 资产
TArray<FAssetData> AssetDataList;
AssetRegistry.GetAssetsByClass(UQuestAsset::StaticClass()->GetClassPathName(), AssetDataList, true);
// 检查是否有其他资产使用了相同的 QuestID
for (const FAssetData& AssetData : AssetDataList)
{
// 跳过自己
if (AssetData.GetAsset() == this)
{
continue;
}
if (UQuestAsset* OtherQuestAsset = Cast<UQuestAsset>(AssetData.GetAsset()))
{
if (OtherQuestAsset->QuestID == this->QuestID)
{
// 发现重复的 QuestID显示警告并建议修改
FText WarningTitle = FText::FromString(TEXT("QuestID 重复警告"));
FText WarningMessage = FText::FromString(FString::Printf(
TEXT("QuestID %d 已被其他任务资产使用:\n%s\n\n请修改为不同的ID以避免冲突。"),
this->QuestID,
*AssetData.AssetName.ToString()
));
FMessageDialog::Open(EAppMsgType::Ok, WarningMessage, WarningTitle);
UE_LOG(LogTemp, Warning, TEXT("QuestID %d 重复!已存在于资产: %s"),
this->QuestID, *AssetData.AssetName.ToString());
break;
}
}
}
}
}
void UQuestAsset::PostDuplicate(bool bDuplicateForPIE)
{
Super::PostDuplicate(bDuplicateForPIE);
if (!bDuplicateForPIE)
{
int32 MaxQuestID = 0;
// 获取资产注册表模块
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
// 查找所有 QuestAsset 资产
TArray<FAssetData> AssetDataList;
AssetRegistry.GetAssetsByClass(UQuestAsset::StaticClass()->GetClassPathName(), AssetDataList, true);
// 遍历所有已存在的任务资产,找到最大的 QuestID
for (const FAssetData& AssetData : AssetDataList)
{
if (UQuestAsset* QuestAsset = Cast<UQuestAsset>(AssetData.GetAsset()))
{
if (QuestAsset->QuestID > MaxQuestID)
{
MaxQuestID = QuestAsset->QuestID;
}
}
}
QuestID += 1;
}
}
#endif

View File

@ -0,0 +1,58 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "../Quest/QuestTypes.h"
#include "QuestAsset.generated.h"
/**
*
*
*/
UCLASS(BlueprintType)
class PROJECTFISH_API UQuestAsset : public UDataAsset
{
GENERATED_BODY()
public:
#if WITH_EDITOR
// 编辑器中属性修改后的回调
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
virtual void PostDuplicate(bool bDuplicateForPIE) override;
#endif
/** 任务编号ID */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Basic")
int32 QuestID = 0;
/** 任务名称 */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Basic")
FText QuestName;
/** 任务描述 */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Basic", meta = (MultiLine = true))
FText QuestDescription;
/** 前置任务ID列表 */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Trigger", meta = (ToolTip = "完成这些任务后才能触发当前任务, 不填写默认激活接受"))
TArray<int32> PrerequisiteQuestIDs;
/** 任务触发条件(预留,可在蓝图中实现) */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Trigger")
FString TriggerCondition;
/** 任务目标列表 */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Objectives")
TArray<FQuestObjective> Objectives;
/** 任务奖励列表 */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Rewards")
TArray<FQuestReward> Rewards;
/** 任务完成条件描述(预留,可在蓝图中实现) */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Completion")
FString CompletionCondition;
};

View File

@ -3,22 +3,37 @@
#include "GameInfoManager.h"
#include "QuestManager.h"
#include "Kismet/GameplayStatics.h"
FString UGameInfoManager::SaveGameSlotName = TEXT("GameInfo");
void UGameInfoManager::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
Collection.InitializeDependency<UQuestManager>();
if (!LoadGameInfo())
{
UE_LOG(LogTemp, Warning, TEXT("本地存档不存在加载失败"));
}
}
void UGameInfoManager::NewSaveGame()
{
UGameplayStatics::DeleteGameInSlot(SaveGameSlotName, 0);
}
void UGameInfoManager::SaveGameInfo()
{
if (IsValid(PlayerInfo.Get()))
if (!IsValid(PlayerInfo.Get()))
{
PlayerInfo = NewObject<UPlayerInfoSaveGame>(this);
}
// 保存任务数据
if (UQuestManager* QuestManager = GetGameInstance()->GetSubsystem<UQuestManager>())
{
PlayerInfo->QuestSaveData = QuestManager->GetQuestSaveData();
}
if (UGameplayStatics::SaveGameToSlot(PlayerInfo.Get(), SaveGameSlotName, 0))
{
UE_LOG(LogTemp, Warning, TEXT("存档保存成功"));
@ -27,11 +42,6 @@ void UGameInfoManager::SaveGameInfo()
{
UE_LOG(LogTemp, Warning, TEXT("存档保存失败"));
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("存档保存失败, PlayerInfo为空"));
}
}
bool UGameInfoManager::LoadGameInfo()
@ -39,8 +49,26 @@ bool UGameInfoManager::LoadGameInfo()
if (UGameplayStatics::DoesSaveGameExist(SaveGameSlotName, 0))
{
PlayerInfo = Cast<UPlayerInfoSaveGame>(UGameplayStatics::LoadGameFromSlot(SaveGameSlotName, 0));
// 加载任务数据
if (UQuestManager* QuestManager = GetGameInstance()->GetSubsystem<UQuestManager>())
{
if (PlayerInfo && GetGameInstance())
{
QuestManager->LoadFromSaveData(PlayerInfo->QuestSaveData);
}
}
return true;
}
else
{
// 加载任务数据
if (UQuestManager* QuestManager = GetGameInstance()->GetSubsystem<UQuestManager>())
{
TArray<FQuestSaveData> SaveData;
QuestManager->LoadFromSaveData(SaveData);
}
}
return false;
}

View File

@ -17,7 +17,9 @@ class PROJECTFISH_API UGameInfoManager : public UGameInstanceSubsystem
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
protected:
public:
UFUNCTION(BlueprintCallable)
void NewSaveGame();
UFUNCTION(BlueprintCallable)
void SaveGameInfo();
UFUNCTION(BlueprintCallable)
@ -30,5 +32,6 @@ protected:
protected:
UPROPERTY(BlueprintReadOnly)
TObjectPtr<class UPlayerInfoSaveGame> PlayerInfo;
static FString SaveGameSlotName;
};

View File

@ -0,0 +1,449 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "QuestManager.h"
#include "GameInfoManager.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Engine/AssetManager.h"
#include "ProjectFish/DataAsset/QuestAsset.h"
#include "ProjectFish/Quest/QuestTypes.h"
void UQuestManager::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
}
void UQuestManager::Deinitialize()
{
Super::Deinitialize();
ActiveQuestsMap.Empty();
CompletedQuestIDs.Empty();
}
void UQuestManager::LoadAllQuests()
{
if (QuestAssets.Num() == 0)
{
// 获得所有任务资源id
TArray<FPrimaryAssetId> QuestAssetIds;
UAssetManager::Get().GetPrimaryAssetIdList(TEXT("QuestAsset"), QuestAssetIds);
// 异步加载所有任务资产
TSharedPtr<FStreamableHandle> LoadHandle = UAssetManager::Get().LoadPrimaryAssets(QuestAssetIds);
if (LoadHandle.IsValid())
{
LoadHandle->WaitUntilComplete();
TArray<UObject*> Assets;
LoadHandle->GetLoadedAssets(Assets); // 获取所有加载成功的资源
for (auto questAsset : Assets)
{
QuestAssets.Add(Cast<UQuestAsset>(questAsset));
}
}
}
}
void UQuestManager::CheckAcceptableQuests(bool bSaveGameInfo)
{
for (auto QuestAsset : QuestAssets)
{
if (!ActiveQuestsMap.Find(QuestAsset->QuestID) && !CompletedQuestIDs.Contains(QuestAsset->QuestID))
{
//任务不在当前已激活的任务 和 已完成的任务列表中
AcceptQuest(QuestAsset, bSaveGameInfo);
}
}
}
bool UQuestManager::AcceptQuest(UQuestAsset* QuestAsset, bool bSaveGameInfo )
{
if (!QuestAsset)
{
return false;
}
// 检查是否已经接受或完成
if (ActiveQuestsMap.Contains(QuestAsset->QuestID) || CompletedQuestIDs.Contains(QuestAsset->QuestID))
{
return false;
}
// 检查前置任务
if (!CanAcceptQuest(QuestAsset))
{
return false;
}
// 创建运行时数据
FQuestRuntimeData RuntimeData = CreateRuntimeData(QuestAsset);
RuntimeData.State = EQuestState::InProgress;
ActiveQuestsMap.Add(QuestAsset->QuestID, RuntimeData);
// 触发事件
OnQuestStateChanged.Broadcast(QuestAsset->QuestID);
UE_LOG(LogTemp, Warning, TEXT("Quest %s Accepted"), *QuestAsset->QuestName.ToString());
if (bSaveGameInfo)
{
//更新存档数据
GetGameInstance()->GetSubsystem<UGameInfoManager>()->SaveGameInfo();
}
return true;
}
bool UQuestManager::FollowQuest(int32 QuestID, bool Follow)
{
FQuestRuntimeData* RuntimeData = ActiveQuestsMap.Find(QuestID);
if (!RuntimeData)
{
return false;
}
if (Follow)
{
FollowingQuestIDs.Add(QuestID);
}
else
{
FollowingQuestIDs.Remove(QuestID);
}
//更新存档数据
GetGameInstance()->GetSubsystem<UGameInfoManager>()->SaveGameInfo();
return true;
}
bool UQuestManager::CompleteQuest(int32 QuestID)
{
FQuestRuntimeData* RuntimeData = ActiveQuestsMap.Find(QuestID);
if (!RuntimeData)
{
return false;
}
// 检查所有目标是否完成
for (const FQuestProgress& Progress : RuntimeData->ObjectiveProgress)
{
if (!Progress.bCompleted)
{
return false;
}
}
// 标记为已完成
RuntimeData->State = EQuestState::Completed;
CompletedQuestIDs.Add(QuestID);
// 触发事件
OnQuestStateChanged.Broadcast(QuestID);
UE_LOG(LogTemp, Warning, TEXT("Quest id = %d Complete"), QuestID);
// // 从活动任务中移除
// ActiveQuestsMap.Remove(QuestID);
//更新存档数据
GetGameInstance()->GetSubsystem<UGameInfoManager>()->SaveGameInfo();
return true;
}
bool UQuestManager::AbandonQuest(int32 QuestID)
{
if (!ActiveQuestsMap.Contains(QuestID))
{
return false;
}
ActiveQuestsMap.Remove(QuestID);
OnQuestStateChanged.Broadcast(QuestID);
return true;
}
void UQuestManager::UpdateObjectiveProgress(int32 QuestID, int32 ObjectiveIndex, int32 Progress)
{
FQuestRuntimeData* RuntimeData = ActiveQuestsMap.Find(QuestID);
if (!RuntimeData || !RuntimeData->QuestAsset)
{
return;
}
// 检查索引有效性
if (!RuntimeData->ObjectiveProgress.IsValidIndex(ObjectiveIndex))
{
return;
}
// 获取目标配置
const FQuestObjective& Objective = RuntimeData->QuestAsset->Objectives[ObjectiveIndex];
FQuestProgress& ProgressData = RuntimeData->ObjectiveProgress[ObjectiveIndex];
// 更新进度
ProgressData.CurrentProgress = FMath::Clamp(Progress, 0, Objective.RequiredAmount);
// 检查是否完成
if (ProgressData.CurrentProgress >= Objective.RequiredAmount)
{
ProgressData.bCompleted = true;
}
// 触发事件
OnQuestObjectiveUpdated.Broadcast(QuestID, ObjectiveIndex);
// 检查任务是否完成
CheckQuestCompletion(QuestID);
}
void UQuestManager::UpdateObjectiveByTargetID(FGameplayTag TargetTag, int32 ProgressDelta)
{
// 遍历所有活动任务,查找匹配的目标
for (TPair<int32, FQuestRuntimeData>& Pair : ActiveQuestsMap)
{
FQuestRuntimeData& RuntimeData = Pair.Value;
if (!RuntimeData.QuestAsset)
{
continue;
}
// 查找匹配的目标
for (int32 i = 0; i < RuntimeData.QuestAsset->Objectives.Num(); ++i)
{
const FQuestObjective& Objective = RuntimeData.QuestAsset->Objectives[i];
if (Objective.TargetID.TargetTag == TargetTag)
{
// 增加进度
int32 NewProgress = RuntimeData.ObjectiveProgress[i].CurrentProgress + ProgressDelta;
UpdateObjectiveProgress(Pair.Key, i, NewProgress);
}
}
}
//检测是否有其他可接受的任务
CheckAcceptableQuests();
}
EQuestState UQuestManager::GetQuestState(int32 QuestID) const
{
if (CompletedQuestIDs.Contains(QuestID))
{
return EQuestState::Completed;
}
const FQuestRuntimeData* RuntimeData = ActiveQuestsMap.Find(QuestID);
if (RuntimeData)
{
return RuntimeData->State;
}
return EQuestState::NotStarted;
}
bool UQuestManager::IsQuestFollowing(int32 QuestID) const
{
return FollowingQuestIDs.Contains(QuestID);
}
TArray<UQuestAsset*> UQuestManager::GetActiveQuests() const
{
TArray<UQuestAsset*> Result;
for (const TPair<int32, FQuestRuntimeData>& Pair : ActiveQuestsMap)
{
if (Pair.Value.State == EQuestState::InProgress && Pair.Value.QuestAsset)
{
Result.Add(Pair.Value.QuestAsset);
}
}
return Result;
}
TArray<UQuestAsset*> UQuestManager::GetFollowingQuests() const
{
TArray<UQuestAsset*> Result;
for (const TPair<int32, FQuestRuntimeData>& Pair : ActiveQuestsMap)
{
if (FollowingQuestIDs.Contains(Pair.Key))
{
Result.Add(Pair.Value.QuestAsset);
}
}
return Result;
}
TArray<UQuestAsset*> UQuestManager::GetCompletedQuests() const
{
TArray<UQuestAsset*> Result;
for (const TPair<int32, FQuestRuntimeData>& Pair : ActiveQuestsMap)
{
if (Pair.Value.State == EQuestState::Completed && Pair.Value.QuestAsset)
{
Result.Add(Pair.Value.QuestAsset);
}
}
return Result;
}
bool UQuestManager::GetQuestProgress(int32 QuestID, TArray<FQuestProgress>& OutProgress) const
{
const FQuestRuntimeData* RuntimeData = ActiveQuestsMap.Find(QuestID);
if (RuntimeData)
{
OutProgress = RuntimeData->ObjectiveProgress;
return true;
}
return false;
}
bool UQuestManager::GetQuestRuntimeData(int32 QuestID, FQuestRuntimeData& OutData) const
{
const FQuestRuntimeData* RuntimeData = ActiveQuestsMap.Find(QuestID);
if (RuntimeData)
{
OutData = *RuntimeData;
return true;
}
return false;
}
bool UQuestManager::CanAcceptQuest(UQuestAsset* QuestAsset) const
{
if (!QuestAsset)
{
return false;
}
// 检查所有前置任务是否完成
for (int32 PrerequisiteID : QuestAsset->PrerequisiteQuestIDs)
{
if (!CompletedQuestIDs.Contains(PrerequisiteID))
{
return false;
}
}
return true;
}
TArray<FQuestSaveData> UQuestManager::GetQuestSaveData() const
{
TArray<FQuestSaveData> SaveDataArray;
for (const TPair<int32, FQuestRuntimeData>& Pair : ActiveQuestsMap)
{
FQuestSaveData SaveData;
SaveData.QuestID = Pair.Key;
SaveData.QuestState = Pair.Value.State;
// 保存目标进度
for (const FQuestProgress& Progress : Pair.Value.ObjectiveProgress)
{
SaveData.ObjectiveProgress.Add(Progress.CurrentProgress);
SaveData.ObjectiveCompleted.Add(Progress.bCompleted);
}
//设置追踪状态
if (FollowingQuestIDs.Contains(Pair.Key))
{
SaveData.bFollowing = true;
}
else
{
SaveData.bFollowing = false;
}
SaveDataArray.Add(SaveData);
}
return SaveDataArray;
}
void UQuestManager::LoadFromSaveData(const TArray<FQuestSaveData>& SaveData)
{
// 清空现有数据
ActiveQuestsMap.Empty();
CompletedQuestIDs.Empty();
FollowingQuestIDs.Empty();
LoadAllQuests();
for (const FQuestSaveData& Data : SaveData)
{
if (Data.QuestState == EQuestState::Completed)
{
CompletedQuestIDs.Add(Data.QuestID);
}
else if (Data.QuestState == EQuestState::InProgress)
{
for (auto questAsset: QuestAssets)
{
if (questAsset->QuestID == Data.QuestID)
{
FQuestRuntimeData RuntimeData = CreateRuntimeData(questAsset);
for (int i =0; i < Data.ObjectiveProgress.Num(); i++)
{
RuntimeData.ObjectiveProgress[i].CurrentProgress = Data.ObjectiveProgress[i];
RuntimeData.ObjectiveProgress[i].bCompleted = Data.ObjectiveCompleted[i];
}
RuntimeData.State = EQuestState::InProgress;
ActiveQuestsMap.Add(questAsset->QuestID, RuntimeData);
//添加追踪任务列表
if (Data.bFollowing)
{
FollowingQuestIDs.Add(questAsset->QuestID);
}
return;
}
}
}
}
CheckAcceptableQuests(true);
}
void UQuestManager::CheckQuestCompletion(int32 QuestID)
{
FQuestRuntimeData* RuntimeData = ActiveQuestsMap.Find(QuestID);
if (!RuntimeData)
{
return;
}
// 检查所有目标是否完成
bool bAllCompleted = true;
for (const FQuestProgress& Progress : RuntimeData->ObjectiveProgress)
{
if (!Progress.bCompleted)
{
bAllCompleted = false;
break;
}
}
// 如果所有目标完成,自动完成任务
if (bAllCompleted)
{
CompleteQuest(QuestID);
}
}
FQuestRuntimeData UQuestManager::CreateRuntimeData(UQuestAsset* QuestAsset)
{
FQuestRuntimeData RuntimeData;
RuntimeData.QuestAsset = QuestAsset;
RuntimeData.State = EQuestState::NotStarted;
// 初始化目标进度
for (int32 i = 0; i < QuestAsset->Objectives.Num(); ++i)
{
FQuestProgress Progress;
Progress.CurrentProgress = 0;
Progress.bCompleted = false;
RuntimeData.ObjectiveProgress.Add(Progress);
}
return RuntimeData;
}

View File

@ -0,0 +1,160 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "../../Quest/QuestTypes.h"
#include "QuestManager.generated.h"
class UQuestAsset;
/**
*
*/
USTRUCT(BlueprintType)
struct FQuestRuntimeData
{
GENERATED_BODY()
/** 任务资产引用 */
UPROPERTY(BlueprintReadOnly)
TObjectPtr<UQuestAsset> QuestAsset;
/** 任务状态 */
UPROPERTY(BlueprintReadWrite)
EQuestState State = EQuestState::NotStarted;
/** 目标进度 */
UPROPERTY(BlueprintReadWrite)
TArray<FQuestProgress> ObjectiveProgress;
};
// 任务事件委托
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnQuestStateChanged, int32, QuestID);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnQuestObjectiveUpdated, int32, QuestID, int32, ObjectiveIndex);
/**
*
*
*/
UCLASS()
class PROJECTFISH_API UQuestManager : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
// ========== 任务操作 ==========
/** 加载本地所有任务 */
void LoadAllQuests();
/** 检测所有可接收的任务 */
UFUNCTION(BlueprintCallable, Category = "Quest")
void CheckAcceptableQuests(bool bSaveGameInfo = false);
/** 接受任务 */
UFUNCTION(BlueprintCallable, Category = "Quest")
bool AcceptQuest(UQuestAsset* QuestAsset, bool bSaveGameInfo = false);
/** 接受任务 */
UFUNCTION(BlueprintCallable, Category = "Quest")
bool FollowQuest(int32 QuestID, bool Follow);
/** 完成任务 */
UFUNCTION(BlueprintCallable, Category = "Quest")
bool CompleteQuest(int32 QuestID);
/** 放弃任务 */
UFUNCTION(BlueprintCallable, Category = "Quest")
bool AbandonQuest(int32 QuestID);
// ========== 任务进度 ==========
/** 更新任务目标进度(收集物品、击败敌人、到达地点) */
UFUNCTION(BlueprintCallable, Category = "Quest")
void UpdateObjectiveProgress(int32 QuestID, int32 ObjectiveIndex, int32 Progress);
/** 通过目标ID更新进度自动查找对应目标 */
UFUNCTION(BlueprintCallable, Category = "Quest")
void UpdateObjectiveByTargetID(FGameplayTag TargetTag, int32 ProgressDelta = 1);
// ========== 任务查询 ==========
/** 获取任务状态 */
UFUNCTION(BlueprintPure, Category = "Quest")
EQuestState GetQuestState(int32 QuestID) const;
/** 任务是否正在被追踪 */
UFUNCTION(BlueprintPure, Category = "Quest")
bool IsQuestFollowing(int32 QuestID) const;
/** 获取所有进行中的任务 */
UFUNCTION(BlueprintPure, Category = "Quest")
TArray<UQuestAsset*> GetActiveQuests() const;
/** 获取所有追踪的任务 */
UFUNCTION(BlueprintPure, Category = "Quest")
TArray<UQuestAsset*> GetFollowingQuests() const;
/** 获取所有已完成的任务 */
UFUNCTION(BlueprintPure, Category = "Quest")
TArray<UQuestAsset*> GetCompletedQuests() const;
/** 获取任务进度 */
UFUNCTION(BlueprintPure, Category = "Quest")
bool GetQuestProgress(int32 QuestID, TArray<FQuestProgress>& OutProgress) const;
/** 获取任务进度 */
UFUNCTION(BlueprintPure, Category = "Quest")
bool GetQuestRuntimeData(int32 QuestID, FQuestRuntimeData& OutData) const;
/** 检查是否可以接受任务(前置任务检查) */
UFUNCTION(BlueprintPure, Category = "Quest")
bool CanAcceptQuest(UQuestAsset* QuestAsset) const;
// ========== 存档相关 ==========
/** 获取所有任务的存档数据 */
UFUNCTION(BlueprintCallable, Category = "Quest")
TArray<FQuestSaveData> GetQuestSaveData() const;
/** 从存档数据加载任务 */
UFUNCTION(BlueprintCallable, Category = "Quest")
void LoadFromSaveData(const TArray<FQuestSaveData>& SaveData);
// ========== 事件 ==========
/** 任务状态改变事件 */
UPROPERTY(BlueprintAssignable, Category = "Quest|Events")
FOnQuestStateChanged OnQuestStateChanged;
/** 任务目标更新事件 */
UPROPERTY(BlueprintAssignable, Category = "Quest|Events")
FOnQuestObjectiveUpdated OnQuestObjectiveUpdated;
protected:
/** 检查任务是否完成 */
void CheckQuestCompletion(int32 QuestID);
/** 初始化任务运行时数据 */
FQuestRuntimeData CreateRuntimeData(UQuestAsset* QuestAsset);
private:
/** 运行时任务数据映射 (QuestID -> RuntimeData) */
UPROPERTY()
TMap<int32, FQuestRuntimeData> ActiveQuestsMap;
/** 正在追踪的任务列表 */
UPROPERTY(BlueprintReadOnly, meta = (AllowPrivateAccess = true))
TSet<int32> FollowingQuestIDs;
/** 已完成的任务ID列表 */
UPROPERTY()
TSet<int32> CompletedQuestIDs;
UPROPERTY()
TArray<UQuestAsset*> QuestAssets;
};

View File

@ -0,0 +1,140 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "QuestTypes.generated.h"
/**
*
*/
UENUM(BlueprintType)
enum class EQuestObjectiveType : uint8
{
CollectItem UMETA(DisplayName = "收集物品并交付"),
DefeatEnemy UMETA(DisplayName = "战胜指定的敌人"),
ReachLocation UMETA(DisplayName = "到达指定地点")
};
/**
*
*/
UENUM(BlueprintType)
enum class EQuestState : uint8
{
NotStarted UMETA(DisplayName = "未开始"),
InProgress UMETA(DisplayName = "进行中"),
Completed UMETA(DisplayName = "已完成"),
Failed UMETA(DisplayName = "失败")
};
/**
*
*/
USTRUCT(BlueprintType)
struct FQuestReward
{
GENERATED_BODY()
/** 奖励物品资产ID */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Quest")
FPrimaryAssetId RewardItemAssetId;
/** 奖励数量 */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Quest")
int32 Amount = 1;
/** 奖励金币 */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Quest")
int32 GoldAmount = 0;
};
/**
*
*/
USTRUCT(BlueprintType)
struct FQuestTargetInfo
{
GENERATED_BODY()
/** 目标名称*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Quest")
FText QuestTargetName;
/** 目标ID */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Quest")
FGameplayTag TargetTag;
};
/**
*
*/
USTRUCT(BlueprintType)
struct FQuestObjective
{
GENERATED_BODY()
/** 目标类型 */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Quest")
EQuestObjectiveType ObjectiveType = EQuestObjectiveType::CollectItem;
/** 目标描述 */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Quest")
FText ObjectiveDescription;
/** 目标ID物品ID、敌人ID、地点Tag等 */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Quest")
FQuestTargetInfo TargetID;
/** 需要完成的数量 */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Quest")
int32 RequiredAmount = 1;
};
/**
*
*/
USTRUCT(BlueprintType)
struct FQuestProgress
{
GENERATED_BODY()
/** 当前进度 */
UPROPERTY(BlueprintReadWrite, Category = "Quest")
int32 CurrentProgress = 0;
/** 是否已完成 */
UPROPERTY(BlueprintReadWrite, Category = "Quest")
bool bCompleted = false;
};
/**
*
*/
USTRUCT(BlueprintType)
struct FQuestSaveData
{
GENERATED_BODY()
/** 任务ID */
UPROPERTY(SaveGame, BlueprintReadWrite)
int32 QuestID = 0;
/** 是否正在追踪 */
UPROPERTY(SaveGame, BlueprintReadWrite)
bool bFollowing = false;
/** 任务状态 */
UPROPERTY(SaveGame, BlueprintReadWrite)
EQuestState QuestState = EQuestState::NotStarted;
/** 目标进度列表 */
UPROPERTY(SaveGame, BlueprintReadWrite)
TArray<int32> ObjectiveProgress;
/** 目标完成状态列表 */
UPROPERTY(SaveGame, BlueprintReadWrite)
TArray<bool> ObjectiveCompleted;
};

View File

@ -0,0 +1,14 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "AssetActions/QuestAssetTypeAction.h"
#include "ProjectFish/DataAsset/QuestAsset.h"
UClass* FQuestAssetTypeAction::GetSupportedClass() const
{
return UQuestAsset::StaticClass();
}
uint32 FQuestAssetTypeAction::GetCategories()
{
return MyAssetCategory;
}

View File

@ -0,0 +1,60 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "Factory/QuestAssetFactory.h"
#include "ProjectFish/DataAsset/QuestAsset.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetRegistry/AssetData.h"
UQuestAssetFactory::UQuestAssetFactory()
{
SupportedClass = UQuestAsset::StaticClass();
bCreateNew = true;
bEditAfterNew = true;
}
UObject* UQuestAssetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags,
UObject* Context, FFeedbackContext* Warn)
{
UQuestAsset* NewQuestAsset = NewObject<UQuestAsset>(InParent, Class, Name, Flags | RF_Transactional);
// 自动生成唯一的 QuestID
if (NewQuestAsset)
{
NewQuestAsset->QuestID = GenerateUniqueQuestID();
}
return NewQuestAsset;
}
int32 UQuestAssetFactory::GenerateUniqueQuestID() const
{
int32 MaxQuestID = 0;
// 获取资产注册表模块
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
// 查找所有 QuestAsset 资产
TArray<FAssetData> AssetDataList;
AssetRegistry.GetAssetsByClass(UQuestAsset::StaticClass()->GetClassPathName(), AssetDataList, true);
// 遍历所有已存在的任务资产,找到最大的 QuestID
for (const FAssetData& AssetData : AssetDataList)
{
if (UQuestAsset* QuestAsset = Cast<UQuestAsset>(AssetData.GetAsset()))
{
if (QuestAsset->QuestID > MaxQuestID)
{
MaxQuestID = QuestAsset->QuestID;
}
}
}
// 返回最大ID + 1如果没有现有任务则从1开始
return MaxQuestID + 1;
}
bool UQuestAssetFactory::ShouldShowInNewMenu() const
{
return true;
}

View File

@ -4,12 +4,13 @@
#include "DataTableRowSelectorCustomization.h"
#include "AssetActions/BagConfigAssetTypeAction.h"
#include "AssetActions/ShapeAssetTypeAction.h"
#include "AssetActions/SkillAssetTypeAction.h"
#include "AssetActions/QuestAssetTypeAction.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "ProjectFish/DataAsset/ShapeAsset.h"
#include "ProjectFish/DataAsset/BagConfigAsset.h"
#include "../Public/Thumbnail/ShapeAssetThumbnailRenderer.h"
#include "../Public/Thumbnail/BagConfigThumbnailRenderer.h"
#include "AssetActions/SkillAssetTypeAction.h"
#include "ProjectFish/DataAsset/BagConfigAsset.h"
#define LOCTEXT_NAMESPACE "FProjectFishEditorModule"
@ -42,7 +43,7 @@ void FProjectFishEditorModule::RegisterAssetTypeActions()
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
IAssetTools& AssetTools = AssetToolsModule.Get();
//注册背包形状的菜单
EAssetTypeCategories::Type BogShapeAssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("BagShape")), FText::FromString(TEXT("BagSystem")));
EAssetTypeCategories::Type BogShapeAssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("BagShape")), FText::FromString(TEXT("ProjectFish")));
//BagShape
TSharedRef<FAssetTypeActions_Base> BagShapeAction = MakeShareable(new FShapeAssetTypeAction(BogShapeAssetCategory));
AssetTools.RegisterAssetTypeActions(BagShapeAction);
@ -58,6 +59,11 @@ void FProjectFishEditorModule::RegisterAssetTypeActions()
TSharedRef<FAssetTypeActions_Base> SkillAssetAction = MakeShareable(new FSkillAssetTypeAction(BogShapeAssetCategory));
AssetTools.RegisterAssetTypeActions(SkillAssetAction);
CreatedAssetTypeActions.Add(SkillAssetAction);
//注册任务资源菜单
TSharedRef<FAssetTypeActions_Base> QuestAssetAction = MakeShareable(new FQuestAssetTypeAction(BogShapeAssetCategory));
AssetTools.RegisterAssetTypeActions(QuestAssetAction);
CreatedAssetTypeActions.Add(QuestAssetAction);
}
void FProjectFishEditorModule::UnregisterAssetTypeActions()

View File

@ -0,0 +1,27 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "AssetTypeActions_Base.h"
/**
*
*/
class PROJECTFISHEDITOR_API FQuestAssetTypeAction : public FAssetTypeActions_Base
{
public:
FQuestAssetTypeAction(EAssetTypeCategories::Type InAssetCategory)
: MyAssetCategory(InAssetCategory)
{
}
virtual FText GetName() const override { return FText::FromString("Quest Asset"); }
virtual FColor GetTypeColor() const override { return FColor(255, 215, 0); } // 金色
virtual UClass* GetSupportedClass() const override;
virtual uint32 GetCategories() override;
virtual bool CanFilter() override { return true; }
private:
EAssetTypeCategories::Type MyAssetCategory;
};

View File

@ -0,0 +1,27 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Factories/Factory.h"
#include "QuestAssetFactory.generated.h"
/**
*
*/
UCLASS()
class PROJECTFISHEDITOR_API UQuestAssetFactory : public UFactory
{
GENERATED_BODY()
public:
UQuestAssetFactory();
// UFactory interface
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
virtual bool ShouldShowInNewMenu() const override;
private:
/** 生成唯一的任务ID */
int32 GenerateUniqueQuestID() const;
};