Files
BlueRoseNote/07-Other/AI/AI Agent/UnrealEngine/Graphify测试结果/AIDM_NPC与修仙系统分析报告_Graphify.md

31 KiB
Raw Blame History

AIDM 项目测试分析报告

生成日期2026-05-30 项目D:\MatrixTA\AIGameDev\AIDM 基于LivingWorldSystem Spec、ARPGGameDevelopment Research Report、修仙流派设计文档


[TOC]


任务 1NPC 系统完整架构

一、分级机制

项目采用 三层 NPC 分级模型(来自 rationale_npc_tier_abc 超边):

级别 名称 特征 数据承载
A 级 核心 NPC玩家、重要角色 完整 3D Actor、行为树、动画、GAS 属性 ARPGCharacterBase + URPGAbilitySystemComponent
B 级 势力 NPC宗门弟子、城镇守卫 轻量 Actor + 基础动画,结合纯数据模拟 ARPGAICharacter + 部分 FLWSNpcData 字段
C 级 泛 NPC平民、路人、妖兽 纯数据 USTRUCT,无 Actor不走 NavMesh六边形坐标跳转 FLWSNpcData(位于 LWSNpcTypes.h

分级依据文档 00_项目总控/游戏核心创意.md 中的"NPC锚点和分级"概念,结合 LivingWorldSystem Spec 中明确的"NPC 纯数据模拟"策略。

二、决策模型

核心架构Utility AI + 性格权重 + 目标驱动

每个 NPC 每日 Tick:
  ┌────────────────────────────────────────────┐
  │  ULWSNpcSimulationSubsystem (UWorldSubsystem) │
  │  ├─ 订阅 URPGGameTimeSubsystem::OnGameTimeChanged │
  │  ├─ SimulateDays(DeltaDays)                   │
  │  └─ 每日遍历 ActiveNpcs                         │
  └──────────────┬─────────────────────────────┘
                 │
                 ▼
  ┌────────────────────────────────────────────┐
  │  ULWSNpcDecisionSystem (决策引擎)               │
  │  ├─ DecayNeeds()    — 需求自然衰减(生存↑/安全↑/社交↑... │
  │  ├─ ScoreAction()   — 对所有可能行为打分        │
  │  │   ├─ 当前需求值 × 行为匹配度                   │
  │  │   └─ 性格权重因子 PersonalityFactor          │
  │  └─ ApplyActionToBehavior() — 选最高分执行      │
  └──────────────┬─────────────────────────────┘
                 │
                 ▼
  ELWSNpcAction { Idle, Wander, MoveTo, Trade, Fight, Socialize, Cultivate, ... }

性格三维度FLWSNpcPersonality,位于 LWSNpcPersonalityTypes.h

  • 冷 ↔ 热:影响社交行为权重
  • 慎 ↔ 莽:影响战斗/探索行为权重
  • 私 ↔ 义:影响帮助盟友 vs 追求私利

需求六维度ELWSNpcNeed):生存、安全、社交、权力、资源、修炼

决策实现位置

  • Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcDecisionSystem.h
  • Plugins/RPGGameCore/Source/LivingWorldSystem/Private/LWSNpcDecisionSystem.cpp

三、与时间系统的关系

时间模型:回合+实时混合制,日为原子单位Community 15策划世界系统

URPGGameTimeSubsystem (UGameInstanceSubsystem跨 World 持续)
  │
  ├─ FSDHGameRawTime / FSDHGameTime (30天/月, 12月/年)
  ├─ OnGameTimeChanged 委托广播
  ├─ 世界时间推进 → AddGameTime(DeltaDays)
  │
  └──→ ULWSNpcSimulationSubsystem::SimulateDays(DeltaDays)
       ├─ 每日遍历所有 NPC
       ├─ Wander 算法WanderStayProbability 概率不动,否则随机六邻一步)
       ├─ 调用决策系统 DecideAction
       └─ 缓存轨迹(环形 buffer 32 步)

关键时间常量

  • 1 日 = 最小原子单位
  • 2 帧完成一轮完整 NPC 模拟60 FPS 下每帧处理 1/30 NPC
  • NPC 模拟在后台线程批量执行

四、存档方式

双层存档架构Community 5 + Community 15

第一层:世界永久层(跨存档持久化)
  │
  ├─ LWSWorldSaveGame (USaveGame 子类)
  │   ├─ CachedTileCoords / TileBuffer     → 瓦片数据
  │   ├─ CachedRivers / CachedLakes        → 水文数据
  │   ├─ CachedFactions                    → 势力数据
  │   ├─ CachedNpcs                        → NPC 数据 (TArray<FLWSNpcData>)
  │   ├─ CachedSpiritualVeins              → 灵脉数据
  │   └─ CachedSettlements / Resources     → 聚落/资源数据
  │
  └─ 世界编年史(跨存档的人生记录,待阶段二实现)

第二层:角色存档层(可删除/重建)
  │
  └─ URPGPlayerSaveGame
      ├─ RPGAttributeData                  → FRPGCharacterAttributeData
      ├─ SkillTreeDataPool                → 技能树学习状态
      ├─ RPGSkilllActionPoseTree           → 架势-技能装配绑定
      ├─ RPGItemDataMap                    → 物品容器数据
      └─ TalentComponentSaveData           → 天赋数据

存档关键实现

  • ALWSWorldGenerator::SaveWorld() 写入 ULWSWorldSaveGame(蓝图可调用)
  • ALWSWorldGenerator::LoadWorld() 从存档完整还原世界
  • NPC 数据通过 ULWSNpcSimulationSubsystem::GetActiveNpcsCopy() / RestoreNpcsFromSave() 同步
  • 角色存档删除后,世界永久层不受影响——势力变迁、NPC 死亡/成长、资源消耗写入世界层

任务 2新增「商队 NPC」跨系统依赖分析

一、需要修改的模块及文件

1. NPC 数据结构层(核心修改)

文件 修改内容
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcTypes.h FLWSNpcData 新增字段:TradeRouteIndex(商队路线 IDTradeGoods(货物数组 TMapTradeFactionOrigin/TradeFactionDestination(起止势力)、TradeSpeed(行进速度,单位 hex/日)
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcPersonalityTypes.h 新增 ELWSNpcBehavior::Trade 行为枚举值;新增 ELWSNpcRole::CaravanTrader 角色类型

2. 行为/决策系统

文件 修改内容
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcDecisionSystem.h DecideAction() 新增商队逻辑:到达目的地时触发交易结算、反向切换目的地
Plugins/RPGGameCore/Source/LivingWorldSystem/Private/LWSNpcDecisionSystem.cpp 实现 ScoreAction_Trade():需求权重(资源需求×商队距离评分)
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcSimulationSubsystem.h SimulateDays() 新增商队每日推进:沿 A* 路径逐格移动(有别于 Wander 随机)

3. 势力与生成

文件 修改内容
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcGenerator.h/.cpp GenerateNpcs() 新增商队生成逻辑:势力边界间配对生成商队 NPC泊松碟约束 + 路线预计算
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSFactionTypes.h FLWSFaction 新增 TradePartners(贸易伙伴势力 ID 列表)、TradeRouteFrequencies(每条路线商队生成频率)
Plugins/RPGGameCore/Source/LivingWorldSystem/Private/LWSFactionGenerator.cpp 势力生成后自动配对邻近势力建立贸易路线

4. 寻路系统

文件 修改内容
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSPathfinder.h 商队寻路需在 FLWSPathfindingParams 中支持路线成本(避开高危势力区、倾向道路/平原)

5. 存档

文件 修改内容
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSWorldSaveGame.h FLWSNpcData 新增字段会自然序列化;新增 CachedTradeRoutesTArray<FLWSTradeRoute> 路线拓扑数据)
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSTradeTypes.h 新建FLWSTradeGoods(物品 ID + 数量)、FLWSTradeRoute(起始势力、途经 hex 坐标数组、路线等级)

6. 调试可视化

文件 修改内容
Plugins/RPGGameCore/Source/LivingWorldSystem/Private/LWSWorldGenerator.cpp 新增 DebugDrawTradeRoutes():商队路线以黄色虚线绘制;商队位置以金色圆点标记
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSResourceTypes.h ELWSDebugViewMode 新增 TradeRoutes 枚举

二、存档结构变化

不需要破坏性变更FLWSNpcData 的字段扩展会被 UE 序列化自动兼容USTRUCT 标记 UPROPERTY 的字段)。需要新增:

// 新建 LWSTradeTypes.h
USTRUCT()
struct FLWSTradeRoute
{
    UPROPERTY() int32 RouteId;
    UPROPERTY() int32 OriginFactionId;
    UPROPERTY() int32 DestinationFactionId;
    UPROPERTY() TArray<FLWSAxialCoord> Waypoints;  // A* 预计算路径
    UPROPERTY() float RouteLevel;                    // 路线等级(影响货物品质)
};

// LWSWorldSaveGame 新增
UPROPERTY() TArray<FLWSTradeRoute> CachedTradeRoutes;

双层存档不受影响:商队 NPC 属于 CachedNpcs(世界永久层),角色存档删除不影响商队。

三、对 GAS 属性系统的影响

不影响 GAS 属性系统。 原因:

  1. 商队 NPC 是纯数据 C 级 NPCFLWSNpcData),不持有 URPGAbilitySystemComponent,不参与 GAS Attribute 同步
  2. 商队交易结算是数值运算(货物价值 = 距离 × 势力稀缺度 × 路线等级),不涉及战斗伤害管线
  3. 如果未来需要"商队被劫掠"战斗,才需要通过 C→B 级 NPC 升级机制接入 GAS

任务 3为「御剑飞行」新增 ActionPose

设计分析

当前 ELM_ActionPose 已有 AirFlying(空行)作为通用空中飞行态,但御剑飞行是剑修专属的战斗力+移动融合态:站在飞剑上高速移动,可在飞行中发起剑技攻击。需要作为独立主架势加入。

状态流转:

地面起势 (GroundDefault)
  │ 释放御剑飞行技能
  ▼
御剑飞行 (AirSwordFlight)         ← 新增
  │
  ├── 输入方向 → 高速移动(飞行移动速度 ×2.5
  ├── 左键 (SubPose0) → 飞剑斩(空中剑技连段)
  ├── 右键 (SubPose1) → 剑罡护体
  ├── 闪避键 → 御剑急转 (AirSwordFlight → AirDash)
  │
  ├── 耐力耗尽/被击落 → AirFalling
  └── 主动取消/落地 → GroundLanding

1. RPGTypes.h — 新增枚举值

// RPGTypes.h — 在 ELM_ActionPose 枚举中 AirDash 之后插入
enum class ELM_ActionPose : uint8
{
    // ... 现有值不变 ...
    AirDash,            // 空冲(飞行状态下的快速冲击)

    // === 御剑飞行(新增)===
    AirSwordFlight,     // 御剑飞行态:剑修站在飞剑上高速移动+空中战斗

    AirSpring,          // 空遁
    // ... 后续值不变 ...
};

2. RPGSwordFlightAbility.h — 御剑飞行 GA 头文件(新建)

// Plugins/RPGGameCore/Source/RPGGameplayAbility/Public/Abilities/RPGSwordFlightAbility.h

#pragma once

#include "SDHGameplayAbility.h"
#include "GameplayTagContainer.h"
#include "RPGSwordFlightAbility.generated.h"

class AWeaponBase;
class UNiagaraSystem;
class UGameplayEffect;

/**
 * 御剑飞行 GameplayAbility
 * Lifecycle: ActivateAbility → UpdateAbility(每帧) → EndAbility
 * 角色站在飞剑上进入 AirSwordFlight 架势,高速飞行 + 空中剑技
 */
UCLASS(BlueprintType, Blueprintable)
class RPGGAMEPLAYABILITY_API URPGSwordFlightAbility : public URPGGameplayAbility
{
    GENERATED_BODY()

public:
    URPGSwordFlightAbility();

    virtual void ActivateAbility() override;
    virtual void UpdateAbility(float DeltaTime) override;
    virtual void EndAbility() override;

    // === 配置属性 ===
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Movement")
    float FlightSpeed = 2000.f;                    // 基础飞行速度 (cm/s)

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Movement")
    float MaxAcceleration = 3000.f;                // 最大加速度

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Movement")
    float TurnRate = 180.f;                        // 转向速率 (deg/s)

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Resource")
    float ManaDrainPerSecond = 8.f;                // 每秒法力消耗

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Resource")
    float MinManaToActivate = 30.f;                // 最低法力阈值(低于此值强制解除)

    // === 视觉 ===
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Visual")
    TSubclassOf<AActor> SwordFlightActorClass;     // 飞剑 Actor附着到角色脚下

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Visual")
    UNiagaraSystem* FlightTrailEffect;             // 飞行拖尾 Niagara 特效

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Visual")
    FRotator SwordMountRotation = FRotator(0.f, 0.f, 15.f); // 飞剑倾斜角度

    // === GAS Effect ===
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Effects")
    TSubclassOf<UGameplayEffect> SwordFlightMovementGE; // 修改 MoveSpeed、GravityScale

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Effects")
    TSubclassOf<UGameplayEffect> SwordFlightDamageBonusGE; // 飞剑期间剑技伤害 +25%

    // === 蓝图事件 ===
    UFUNCTION(BlueprintImplementableEvent, Category = "SwordFlight")
    void OnSwordMounted(AActor* SwordActor);

    UFUNCTION(BlueprintImplementableEvent, Category = "SwordFlight")
    void OnSwordDismounted();

    UFUNCTION(BlueprintImplementableEvent, Category = "SwordFlight")
    void OnFlightManaDepleted();                   // 法力耗尽事件

private:
    // === 内部状态 ===
    UPROPERTY()
    TObjectPtr<AActor> MountedSword;               // 当前挂载的飞剑 Actor

    UPROPERTY()
    TObjectPtr<UNiagaraComponent> TrailNiagara;    // 拖尾特效组件

    UPROPERTY()
    FActiveGameplayEffectHandle MovementGEHandle;  // 移动 GE 句柄

    UPROPERTY()
    FActiveGameplayEffectHandle DamageBonusHandle; // 伤害加成 GE 句柄

    bool bIsActive = false;
    float CurrentManaDrainAccum = 0.f;             // 法力消耗累加器

    // --- 内部方法 ---
    void SpawnAndMountSword();
    void ApplyFlightEffects();
    void RemoveFlightEffects();
    bool CheckManaSufficient() const;
    void DrainManaPerTick(float DeltaTime);
};

3. RPGSwordFlightAbility.cpp — 关键逻辑实现(核心)

// Plugins/RPGGameCore/Source/RPGGameplayAbility/Private/Abilities/RPGSwordFlightAbility.cpp

#include "Abilities/RPGSwordFlightAbility.h"
#include "Character/RPGCharacterBase.h"
#include "Character/Attribute/RPGAttributeSet.h"
#include "Character/Attribute/RPGAttributeComponent.h"
#include "NiagaraComponent.h"
#include "NiagaraFunctionLibrary.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "AbilitySystemComponent.h"

URPGSwordFlightAbility::URPGSwordFlightAbility()
{
    // 标记为持续型 Ability非一次性瞬发
}

void URPGSwordFlightAbility::ActivateAbility()
{
    Super::ActivateAbility();

    ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
    if (!Owner || !CheckManaSufficient())
    {
        SetAbilityValid(false);
        return;
    }

    // 1. 生成飞剑并挂载到角色脚底
    SpawnAndMountSword();

    // 2. 应用飞行 GE修改 CMC MoveSpeed、GravityScale
    ApplyFlightEffects();

    // 3. 角色进入飞行模式
    if (UCharacterMovementComponent* CMC = Owner->GetCharacterMovement())
    {
        CMC->SetMovementMode(MOVE_Flying);
        CMC->MaxFlySpeed = FlightSpeed;
        CMC->MaxAcceleration = MaxAcceleration;
        CMC->BrakingDecelerationFlying = 1000.f;
    }

    // 4. 设置架势为 御剑飞行
    if (USkillSMInstance* SM = Owner->GetSkillSMInstance())
    {
        SM->SetActionPose(ELM_ActionPose::AirSwordFlight);
    }

    // 5. 生成长剑拖尾特效
    if (FlightTrailEffect)
    {
        TrailNiagara = UNiagaraFunctionLibrary::SpawnSystemAttached(
            FlightTrailEffect,
            Owner->GetRootComponent(),
            NAME_None,
            FVector::ZeroVector,
            FRotator::ZeroRotator,
            EAttachLocation::KeepRelativeOffset,
            true
        );
    }

    bIsActive = true;
    SetAbilityValid(true);
}

void URPGSwordFlightAbility::UpdateAbility(float DeltaTime)
{
    Super::UpdateAbility(DeltaTime);
    if (!bIsActive) return;

    // 法力消耗检查
    DrainManaPerTick(DeltaTime);
    if (!CheckManaSufficient())
    {
        OnFlightManaDepleted();
        EndAbility();
        return;
    }

    // 可选:根据输入方向调整飞剑倾斜
    // 通过蓝图 OnFlightUpdate 事件暴露给设计师微调
}

void URPGSwordFlightAbility::EndAbility()
{
    Super::EndAbility();

    ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
    if (!Owner) return;

    // 1. 恢复地面移动模式
    if (UCharacterMovementComponent* CMC = Owner->GetCharacterMovement())
    {
        CMC->SetMovementMode(MOVE_Walking);
        CMC->MaxFlySpeed = 600.f; // 恢复默认
    }

    // 2. 移除 GE
    RemoveFlightEffects();

    // 3. 销毁拖尾
    if (TrailNiagara)
    {
        TrailNiagara->Deactivate();
        TrailNiagara = nullptr;
    }

    // 4. 拆卸飞剑 → 切换到 AirFalling
    OnSwordDismounted();
    if (MountedSword)
    {
        MountedSword->Destroy();
        MountedSword = nullptr;
    }

    // 5. 架势切换:御剑 → 空中下落
    if (USkillSMInstance* SM = Owner->GetSkillSMInstance())
    {
        SM->SetActionPose(ELM_ActionPose::AirFalling);
    }

    bIsActive = false;
}

// ============ 内部实现 ============

void URPGSwordFlightAbility::SpawnAndMountSword()
{
    ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
    if (!Owner || !SwordFlightActorClass) return;

    FActorSpawnParameters SpawnParams;
    SpawnParams.Owner = Owner;
    SpawnParams.Instigator = Owner;

    // 飞剑生成在角色脚下
    FVector SpawnLoc = Owner->GetActorLocation() + FVector(0, 0, -70.f);
    MountedSword = GetWorld()->SpawnActor<AActor>(
        SwordFlightActorClass, SpawnLoc, FRotator::ZeroRotator, SpawnParams
    );

    if (MountedSword)
    {
        MountedSword->AttachToComponent(
            Owner->GetRootComponent(),
            FAttachmentTransformRules::KeepRelativeTransform
        );
        MountedSword->SetActorRelativeLocation(FVector(0, 0, -70.f));
        MountedSword->SetActorRelativeRotation(SwordMountRotation);
        OnSwordMounted(MountedSword);
    }
}

void URPGSwordFlightAbility::ApplyFlightEffects()
{
    ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
    if (!Owner) return;

    URPGAbilitySystemComponent* ASC = Owner->GetRPGAbilitySystemComponent();
    if (!ASC) return;

    // 施加飞行移动 GE
    if (SwordFlightMovementGE)
    {
        MovementGEHandle = ASC->ApplyGameplayEffectToSelf(
            SwordFlightMovementGE->GetDefaultObject<UGameplayEffect>(),
            1.f, ASC->MakeEffectContext()
        );
    }

    // 施加伤害加成 GE
    if (SwordFlightDamageBonusGE)
    {
        DamageBonusHandle = ASC->ApplyGameplayEffectToSelf(
            SwordFlightDamageBonusGE->GetDefaultObject<UGameplayEffect>(),
            1.f, ASC->MakeEffectContext()
        );
    }
}

void URPGSwordFlightAbility::RemoveFlightEffects()
{
    ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
    if (!Owner) return;

    URPGAbilitySystemComponent* ASC = Owner->GetRPGAbilitySystemComponent();
    if (!ASC) return;

    if (MovementGEHandle.IsValid())
        ASC->RemoveActiveGameplayEffect(MovementGEHandle);
    if (DamageBonusHandle.IsValid())
        ASC->RemoveActiveGameplayEffect(DamageBonusHandle);
}

bool URPGSwordFlightAbility::CheckManaSufficient() const
{
    ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
    if (!Owner) return false;

    URPGAbilitySystemComponent* ASC = Owner->GetRPGAbilitySystemComponent();
    if (!ASC) return false;

    return ASC->GetNumericAttribute(URPGAttributeSet::GetManaAttribute()) >= MinManaToActivate;
}

void URPGSwordFlightAbility::DrainManaPerTick(float DeltaTime)
{
    CurrentManaDrainAccum += ManaDrainPerSecond * DeltaTime;
    if (CurrentManaDrainAccum >= 1.f)
    {
        int32 DrainAmount = FMath::FloorToInt(CurrentManaDrainAccum);
        CurrentManaDrainAccum -= DrainAmount;

        ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
        if (Owner)
        {
            Owner->GetRPGAbilitySystemComponent()->SetNumericAttributeBase(
                URPGAttributeSet::GetManaAttribute(),
                FMath::Max(0.f, Owner->GetRPGAbilitySystemComponent()->
                    GetNumericAttribute(URPGAttributeSet::GetManaAttribute()) - DrainAmount)
            );
        }
    }
}

任务 4「符修」技能效果系统设计映射到 GAS Effect

设计前提

符修在流派体系中的定位:通过绘制符文符箓将法术封印在纸上,战斗中消耗符箓瞬间释放。核心机制是制符(战前准备)→ 释符(战斗中消耗)。符文效果通过 GAS GameplayEffect 实现。

一、符修核心机制数据模型

FRTalismanData.h新建

// Plugins/RPGGameCore/Source/RPGGameplayAbility/Public/Abilities/Talisman/FRTalismanData.h

#pragma once

#include "GameplayTagContainer.h"
#include "GameplayEffect.h"
#include "FRTalismanData.generated.h"

/** 符箓等级 */
UENUM(BlueprintType)
enum class ERPGTalismanTier : uint8
{
    YellowPaper   UMETA(DisplayName = "黄纸符"),   // 基础符,单次效果
    SilverScript  UMETA(DisplayName = "银篆符"),   // 中级符可叠加2层
    GoldSeal      UMETA(DisplayName = "金印符"),   // 高级符,效果翻倍
    JadeEdict     UMETA(DisplayName = "玉敕符"),   // 顶级符,范围效果
};

/** 符箓触发方式 */
UENUM(BlueprintType)
enum class ERPGTalismanTrigger : uint8
{
    OnUse      UMETA(DisplayName = "主动释放"),    // 主动从快捷栏使用
    OnHit      UMETA(DisplayName = "受击触发"),    // 被攻击时自动触发
    OnLowHP    UMETA(DisplayName = "濒死护身"),    // HP < 30% 自动触发
    OnTimer    UMETA(DisplayName = "定时触发"),    // 定时延迟触发
};

/** 单个符箓效果定义 — 映射到 1 个 GAS GameplayEffect */
USTRUCT(BlueprintType)
struct RPGGAMEPLAYABILITY_API FRPGTalismanEffect
{
    GENERATED_BODY()

    // 符箓配置
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|Identity")
    FName TalismanId;                                   // 符箓唯一 ID

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|Identity")
    FText TalismanName;                                 // 显示名:"烈火符"、"金刚符"

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|Identity")
    ERPGTalismanTier Tier = ERPGTalismanTier::YellowPaper;

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|Identity")
    ERPGTalismanTrigger TriggerType = ERPGTalismanTrigger::OnUse;

    // === GAS 映射 ===
    // 核心:每个符箓效果对应一个 UGameplayEffect 蓝图子类
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|GAS")
    TSubclassOf<UGameplayEffect> PrimaryEffectClass;    // 主 GE修改属性/施加 Tag

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|GAS")
    TSubclassOf<UGameplayEffect> SecondaryEffectClass;  // 副 GE可选范围/连锁效果)

    // SetByCaller 动态参数(符箓等级越高,倍率越大)
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|GAS")
    TMap<FGameplayTag, float> SetByCallerMagnitudes;    // Tag → 基础数值

    // 消耗
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|Cost")
    int32 ManaCost = 20;                                // 释符消耗法力

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|Cost")
    float CooldownSeconds = 3.f;                        // 释符冷却
};

二、符修五大符系 → GAS GameplayEffect 映射表

符系 代表符箓 GAS GE 类型 修改的 Attribute Duration Policy 关键 SetByCaller Tag
攻击符系 烈火符 GE_Talisman_Fire (Instant) Damage (临时变量) Instant SetByCaller.FireDamage
冰封符 GE_Talisman_Ice (HasDuration) MoveSpeed (减速 %) 5s Duration SetByCaller.SlowPercent
雷击符 GE_Talisman_Thunder (Instant) Damage + Tough (削韧) Instant SetByCaller.ThunderDamage
防御符系 金刚符 GE_Talisman_Barrier (HasDuration) Shield (+护盾值) 10s Duration SetByCaller.ShieldAmount
铁壁符 GE_Talisman_IronWall (HasDuration) DefensePower (+%) 15s Duration SetByCaller.DefenseBonus
净身符 GE_Talisman_Cleanse (Instant) 移除 State.Debuff.* Tag Instant
辅助符系 神行符 GE_Talisman_Speed (HasDuration) MoveSpeed (+%) 20s Duration SetByCaller.SpeedBonus
聚灵符 GE_Talisman_Meditate (HasDuration) Mana (回复速率) 30s Duration SetByCaller.ManaRegenRate
破阵符 GE_Talisman_Break (Instant) 移除敌方 Buff Tag Instant SetByCaller.DispelCount
诅咒符系 虚弱符 GE_Talisman_Weaken (HasDuration) AttackPower (-%敌方) 8s Duration SetByCaller.AttackReduce
定身符 GE_Talisman_Root (HasDuration) 施加 State.CrowdControl.Root Tag 3s Duration SetByCaller.RootDuration
噬魂符 GE_Talisman_SoulDrain (HasDuration) 敌方 Health DoT + 自身 Health Heal 6s Duration SetByCaller.DrainAmount
阵法符系 困龙阵符 GE_Talisman_TrapField (Infinite/HasDuration) 范围 MoveSpeed -80% 15s 范围 Duration SetByCaller.TrapRadius
万剑阵符 GE_Talisman_SwordArray (Instant × N) 范围内 N 次 Damage 结算 Instant (周期触发) SetByCaller.PerSwordDamage
回春阵符 GE_Talisman_HealField (Infinite) 范围内友方 Health 持续回复 UntilCancelled SetByCaller.HealPerTick

三、与现有 GAS Effect 架构的融合

3.1 对接伤害管线

符修攻击符接入现有五段伤害管线(来自 Community 5

释符(烈火符)
  │ URPGGameplayAbility::ActivateAbility()
  ├── 构造 FSkillDamageData
  │   ├─ SkillDamage = SetByCaller.FireDamage × TierMultiplier
  │   ├─ SkillToughDamage = SetByCaller.FireDamage × 0.3
  │   └─ HitLevel = ERPGHitLevel::LV1黄纸~ LV3玉敕
  │
  └── ARPGCharacterBase::ReceiveDamageEvent()
      ├── USDHDamageCaculationSetting::CaculateDamage()
      │   ├─ 暴击 / 命中 / 闪避 / 防御减伤
      │   └─ 输出 { ShieldDamage, ToughDamage, HealthDamage }
      ├── 优先扣 Shield → 再扣 Tough → 最后扣 Health
      └── URPGAttributeSet::PostGameplayEffectExecute

3.2 符箓充能与释放系统

// URPGFuXiuAbilityComponent — 符修专属 ASC 扩展
UCLASS()
class URPGFuXiuAbilityComponent : public UActorComponent
{
    // 符箓背包(最多携带 N 张符)
    UPROPERTY()
    TArray<FRPGTalismanEffect> EquippedTalismans;

    // 制符(非战斗状态,消耗材料+法力+时间)
    UFUNCTION(BlueprintCallable)
    FRPGTalismanEffect CraftTalisman(FName TalismanId, ERPGTalismanTier Tier);

    // 释符(战斗中触发 GAS GE
    UFUNCTION(BlueprintCallable)
    bool ActivateTalisman(int32 TalismanIndex, AActor* Target = nullptr);

    // 符箓等级倍率
    float GetTierMultiplier(ERPGTalismanTier Tier) const
    {
        static const TMap<ERPGTalismanTier, float> Multipliers = {
            { ERPGTalismanTier::YellowPaper,  1.0f },
            { ERPGTalismanTier::SilverScript,  1.5f },
            { ERPGTalismanTier::GoldSeal,      2.2f },
            { ERPGTalismanTier::JadeEdict,     3.0f },
        };
        return Multipliers.FindRef(Tier);
    }
};

3.3 SetByCaller 符箓 Tag 体系

// 在 SDHGameTagSettings 中新增符箓专属 SetByCaller Tag
namespace RPGSetByCaller
{
    // 攻击符
    UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Fire_Damage);
    UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Ice_SlowPercent);
    UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Thunder_Damage);

    // 防御符
    UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Barrier_ShieldAmount);
    UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_IronWall_DefenseBonus);

    // 辅助符
    UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Speed_Bonus);
    UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Meditate_ManaRegen);

    // 诅咒符
    UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Weaken_AttackReduce);
    UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_SoulDrain_Amount);

    // 阵法符
    UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Trap_Radius);
    UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_SwordArray_PerSwordDamage);
    UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_HealField_HealPerTick);
}

四、符箓效果示例:「裂空·雷击符」

蓝图配置BP_GE_Talisman_Thunder_Base:

Modifiers:
  ├─ URPGAttributeSet::Damage
  │   ├─ ModifierOp: Override
  │   └─ Magnitude: SetByCaller (Tag=Talisman_Thunder_Damage)
  ├─ URPGAttributeSet::Tough
  │   ├─ ModifierOp: Add
  │   └─ Magnitude: SetByCaller (Tag=Talisman_Thunder_Damage) × -0.3
  └─ GameplayTag:
      └─ Grant: State.HitReaction.Stagger击退硬直

Duration Policy: Instant

Damage 计算链:
  Base Damage = SetByCaller.Talisman_Thunder_Damage (50)
  × TierMultiplier (黄纸=1.0 / 银篆=1.5 / 金印=2.2 / 玉敕=3.0)
  × 符修修为加成 (境界 × 0.05)
  × 当前灵力系数 (Mana/MaxMana × 0.5 + 0.5)

  黄纸雷击符 = 50 × 1.0 × 1.0 × 1.0 = 50 伤害 + 15 削韧
  玉敕雷击符 = 50 × 3.0 × 2.0 × 1.0 = 300 伤害 + 90 削韧

五、涉及文件清单

文件(新建/修改) 内容
Public/Abilities/Talisman/FRTalismanData.h 新建:符箓数据结构、枚举
Public/Abilities/Talisman/URPGFuXiuAbilityComponent.h/.cpp 新建:符修 ASC 扩展组件
Public/Abilities/Talisman/GE_Talisman_*.h 新建5 类 14 个 GameplayEffect 蓝图基类
Public/Config/SDHGameTagSettings.h 修改:新增 SetByCaller.Talisman_* Tag
Public/Character/Attribute/RPGAttributeSet.h 不变:符箓几何修改现有 Health/Shield/Tough/Mana/MoveSpeed/AttackPower/DefensePower 属性
Public/Items/RPGItem.h 修改:新增 URPGTalismanItem 物品类型
.trae/documents/01_策划设计/修仙基础概念设定/修仙流派.md 修改:符修独立为独立流派(当前在魔修路径下)

附录:关键文件速查

缩写 全称 核心文件位置
LWS LivingWorldSystem Plugins/RPGGameCore/Source/LivingWorldSystem/
GA/GAS Gameplay Ability System Plugins/RPGGameCore/Source/RPGGameplayAbility/
ASC AbilitySystemComponent URPGAbilitySystemComponent
GE GameplayEffect UGameplayEffect 蓝图子类
CMC CharacterMovementComponent LCMCharacterMovementComponent
ActionPose 架势(主姿态) ELM_ActionPoseRPGTypes.h:20324 个主架势
SM SkillStateMachine USkillSMInstance (LogicDriver)