diff --git a/ProjectFish/.claude/settings.local.json b/ProjectFish/.claude/settings.local.json new file mode 100644 index 0000000..9541374 --- /dev/null +++ b/ProjectFish/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(dir:*)" + ], + "deny": [], + "ask": [] + } +} diff --git a/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll b/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll index 933102e..d7dd8e9 100644 Binary files a/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll and b/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll differ diff --git a/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFishEditor.dll b/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFishEditor.dll index 0f4953c..1ac9c1e 100644 Binary files a/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFishEditor.dll and b/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFishEditor.dll differ diff --git a/ProjectFish/Source/ProjectFish/Data/PlayerInfoSaveGame.h b/ProjectFish/Source/ProjectFish/Data/PlayerInfoSaveGame.h index 5c83104..5edba24 100644 --- a/ProjectFish/Source/ProjectFish/Data/PlayerInfoSaveGame.h +++ b/ProjectFish/Source/ProjectFish/Data/PlayerInfoSaveGame.h @@ -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 PlayerContainerItems; + + // 任务存档数据 + UPROPERTY(SaveGame, BlueprintReadWrite, meta = (ToolTip = "任务存档数据")) + TArray QuestSaveData; }; diff --git a/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.cpp b/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.cpp new file mode 100644 index 0000000..562914c --- /dev/null +++ b/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.cpp @@ -0,0 +1,58 @@ +// 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("AssetRegistry"); + IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); + + // 查找所有 QuestAsset 资产 + TArray AssetDataList; + AssetRegistry.GetAssetsByClass(UQuestAsset::StaticClass()->GetClassPathName(), AssetDataList, true); + + // 检查是否有其他资产使用了相同的 QuestID + for (const FAssetData& AssetData : AssetDataList) + { + // 跳过自己 + if (AssetData.GetAsset() == this) + { + continue; + } + + if (UQuestAsset* OtherQuestAsset = Cast(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; + } + } + } + } +} +#endif + diff --git a/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.h b/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.h new file mode 100644 index 0000000..9aab783 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.h @@ -0,0 +1,59 @@ +// 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; +#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") + TArray PrerequisiteQuestIDs; + + /** 任务触发条件(预留,可在蓝图中实现) */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Trigger") + FString TriggerCondition; + + /** 任务目标列表 */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Objectives") + TArray Objectives; + + /** 任务奖励列表 */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Rewards") + TArray Rewards; + + /** 任务完成条件描述(预留,可在蓝图中实现) */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Completion") + FString CompletionCondition; + + /** 是否自动触发(游戏开始时自动接取) */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Trigger") + bool bAutoTrigger = false; +}; diff --git a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/GameInfoManager.cpp b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/GameInfoManager.cpp index 0724561..c3f3b34 100644 --- a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/GameInfoManager.cpp +++ b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/GameInfoManager.cpp @@ -3,6 +3,7 @@ #include "GameInfoManager.h" +#include "QuestManager.h" #include "Kismet/GameplayStatics.h" FString UGameInfoManager::SaveGameSlotName = TEXT("GameInfo"); @@ -19,6 +20,12 @@ void UGameInfoManager::SaveGameInfo() { if (IsValid(PlayerInfo.Get())) { + // 保存任务数据 + if (UQuestManager* QuestManager = GetGameInstance()->GetSubsystem()) + { + PlayerInfo->QuestSaveData = QuestManager->GetQuestSaveData(); + } + if (UGameplayStatics::SaveGameToSlot(PlayerInfo.Get(), SaveGameSlotName, 0)) { UE_LOG(LogTemp, Warning, TEXT("存档保存成功")); @@ -39,6 +46,16 @@ bool UGameInfoManager::LoadGameInfo() if (UGameplayStatics::DoesSaveGameExist(SaveGameSlotName, 0)) { PlayerInfo = Cast(UGameplayStatics::LoadGameFromSlot(SaveGameSlotName, 0)); + + // 加载任务数据 + if (PlayerInfo && GetGameInstance()) + { + if (UQuestManager* QuestManager = GetGameInstance()->GetSubsystem()) + { + QuestManager->LoadFromSaveData(PlayerInfo->QuestSaveData); + } + } + return true; } return false; diff --git a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.cpp b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.cpp new file mode 100644 index 0000000..3c6e4d0 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.cpp @@ -0,0 +1,312 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "QuestManager.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(); +} + +bool UQuestManager::AcceptQuest(UQuestAsset* QuestAsset) +{ + 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); + + 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); + + // 从活动任务中移除 + ActiveQuestsMap.Remove(QuestID); + + 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(FName TargetID, int32 ProgressDelta) +{ + // 遍历所有活动任务,查找匹配的目标 + for (TPair& 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 == TargetID) + { + // 增加进度 + int32 NewProgress = RuntimeData.ObjectiveProgress[i].CurrentProgress + ProgressDelta; + UpdateObjectiveProgress(Pair.Key, i, NewProgress); + } + } + } +} + +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; +} + +TArray UQuestManager::GetActiveQuests() const +{ + TArray Result; + for (const TPair& Pair : ActiveQuestsMap) + { + if (Pair.Value.State == EQuestState::InProgress && Pair.Value.QuestAsset) + { + Result.Add(Pair.Value.QuestAsset); + } + } + return Result; +} + +TArray UQuestManager::GetCompletedQuests() const +{ + TArray Result; + for (const TPair& Pair : ActiveQuestsMap) + { + if (Pair.Value.State == EQuestState::Completed && Pair.Value.QuestAsset) + { + Result.Add(Pair.Value.QuestAsset); + } + } + return Result; +} + +bool UQuestManager::GetQuestProgress(int32 QuestID, TArray& OutProgress) const +{ + const FQuestRuntimeData* RuntimeData = ActiveQuestsMap.Find(QuestID); + if (RuntimeData) + { + OutProgress = RuntimeData->ObjectiveProgress; + 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 UQuestManager::GetQuestSaveData() const +{ + TArray SaveDataArray; + + for (const TPair& 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); + } + + SaveDataArray.Add(SaveData); + } + + return SaveDataArray; +} + +void UQuestManager::LoadFromSaveData(const TArray& SaveData) +{ + // 清空现有数据 + ActiveQuestsMap.Empty(); + CompletedQuestIDs.Empty(); + + // 注意:这里需要从资产管理器或配置中加载QuestAsset + // 暂时留空,需要根据项目的资产加载方式实现 + // 建议在GameInstance或ProjectSettings中维护一个QuestAsset列表 + + for (const FQuestSaveData& Data : SaveData) + { + if (Data.QuestState == EQuestState::Completed) + { + CompletedQuestIDs.Add(Data.QuestID); + } + + // TODO: 从资产ID加载QuestAsset并恢复进度 + // 需要实现资产查找逻辑 + } +} + +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; +} diff --git a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.h b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.h new file mode 100644 index 0000000..ecdb17d --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.h @@ -0,0 +1,131 @@ +// 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 QuestAsset; + + /** 任务状态 */ + UPROPERTY(BlueprintReadWrite) + EQuestState State = EQuestState::NotStarted; + + /** 目标进度 */ + UPROPERTY(BlueprintReadWrite) + TArray 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; + + // ========== 任务操作 ========== + + /** 接受任务 */ + UFUNCTION(BlueprintCallable, Category = "Quest") + bool AcceptQuest(UQuestAsset* QuestAsset); + + /** 完成任务 */ + 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(FName TargetID, int32 ProgressDelta = 1); + + // ========== 任务查询 ========== + + /** 获取任务状态 */ + UFUNCTION(BlueprintPure, Category = "Quest") + EQuestState GetQuestState(int32 QuestID) const; + + /** 获取所有进行中的任务 */ + UFUNCTION(BlueprintPure, Category = "Quest") + TArray GetActiveQuests() const; + + /** 获取所有已完成的任务 */ + UFUNCTION(BlueprintPure, Category = "Quest") + TArray GetCompletedQuests() const; + + /** 获取任务进度 */ + UFUNCTION(BlueprintPure, Category = "Quest") + bool GetQuestProgress(int32 QuestID, TArray& OutProgress) const; + + /** 检查是否可以接受任务(前置任务检查) */ + UFUNCTION(BlueprintPure, Category = "Quest") + bool CanAcceptQuest(UQuestAsset* QuestAsset) const; + + // ========== 存档相关 ========== + + /** 获取所有任务的存档数据 */ + UFUNCTION(BlueprintCallable, Category = "Quest") + TArray GetQuestSaveData() const; + + /** 从存档数据加载任务 */ + UFUNCTION(BlueprintCallable, Category = "Quest") + void LoadFromSaveData(const TArray& 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 ActiveQuestsMap; + + /** 已完成的任务ID列表 */ + UPROPERTY() + TSet CompletedQuestIDs; +}; diff --git a/ProjectFish/Source/ProjectFish/Quest/QuestTypes.h b/ProjectFish/Source/ProjectFish/Quest/QuestTypes.h new file mode 100644 index 0000000..0fdb274 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Quest/QuestTypes.h @@ -0,0 +1,117 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.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 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") + FName 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) + EQuestState QuestState = EQuestState::NotStarted; + + /** 目标进度列表 */ + UPROPERTY(SaveGame, BlueprintReadWrite) + TArray ObjectiveProgress; + + /** 目标完成状态列表 */ + UPROPERTY(SaveGame, BlueprintReadWrite) + TArray ObjectiveCompleted; +}; diff --git a/ProjectFish/Source/ProjectFishEditor/Private/AssetActions/QuestAssetTypeAction.cpp b/ProjectFish/Source/ProjectFishEditor/Private/AssetActions/QuestAssetTypeAction.cpp new file mode 100644 index 0000000..81a2741 --- /dev/null +++ b/ProjectFish/Source/ProjectFishEditor/Private/AssetActions/QuestAssetTypeAction.cpp @@ -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; +} diff --git a/ProjectFish/Source/ProjectFishEditor/Private/Factory/QuestAssetFactory.cpp b/ProjectFish/Source/ProjectFishEditor/Private/Factory/QuestAssetFactory.cpp new file mode 100644 index 0000000..c0c2bb3 --- /dev/null +++ b/ProjectFish/Source/ProjectFishEditor/Private/Factory/QuestAssetFactory.cpp @@ -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(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("AssetRegistry"); + IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); + + // 查找所有 QuestAsset 资产 + TArray AssetDataList; + AssetRegistry.GetAssetsByClass(UQuestAsset::StaticClass()->GetClassPathName(), AssetDataList, true); + + // 遍历所有已存在的任务资产,找到最大的 QuestID + for (const FAssetData& AssetData : AssetDataList) + { + if (UQuestAsset* QuestAsset = Cast(AssetData.GetAsset())) + { + if (QuestAsset->QuestID > MaxQuestID) + { + MaxQuestID = QuestAsset->QuestID; + } + } + } + + // 返回最大ID + 1,如果没有现有任务则从1开始 + return MaxQuestID + 1; +} + +bool UQuestAssetFactory::ShouldShowInNewMenu() const +{ + return true; +} diff --git a/ProjectFish/Source/ProjectFishEditor/Private/ProjectFishEditor.cpp b/ProjectFish/Source/ProjectFishEditor/Private/ProjectFishEditor.cpp index 00f5653..0ba8beb 100644 --- a/ProjectFish/Source/ProjectFishEditor/Private/ProjectFishEditor.cpp +++ b/ProjectFish/Source/ProjectFishEditor/Private/ProjectFishEditor.cpp @@ -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("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 BagShapeAction = MakeShareable(new FShapeAssetTypeAction(BogShapeAssetCategory)); AssetTools.RegisterAssetTypeActions(BagShapeAction); @@ -58,6 +59,11 @@ void FProjectFishEditorModule::RegisterAssetTypeActions() TSharedRef SkillAssetAction = MakeShareable(new FSkillAssetTypeAction(BogShapeAssetCategory)); AssetTools.RegisterAssetTypeActions(SkillAssetAction); CreatedAssetTypeActions.Add(SkillAssetAction); + + //注册任务资源菜单 + TSharedRef QuestAssetAction = MakeShareable(new FQuestAssetTypeAction(BogShapeAssetCategory)); + AssetTools.RegisterAssetTypeActions(QuestAssetAction); + CreatedAssetTypeActions.Add(QuestAssetAction); } void FProjectFishEditorModule::UnregisterAssetTypeActions() diff --git a/ProjectFish/Source/ProjectFishEditor/Public/AssetActions/QuestAssetTypeAction.h b/ProjectFish/Source/ProjectFishEditor/Public/AssetActions/QuestAssetTypeAction.h new file mode 100644 index 0000000..678c802 --- /dev/null +++ b/ProjectFish/Source/ProjectFishEditor/Public/AssetActions/QuestAssetTypeAction.h @@ -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; +}; diff --git a/ProjectFish/Source/ProjectFishEditor/Public/Factory/QuestAssetFactory.h b/ProjectFish/Source/ProjectFishEditor/Public/Factory/QuestAssetFactory.h new file mode 100644 index 0000000..1b6c0e2 --- /dev/null +++ b/ProjectFish/Source/ProjectFishEditor/Public/Factory/QuestAssetFactory.h @@ -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; +};