using Opsive.BehaviorDesigner.Runtime.Tasks; using Opsive.BehaviorDesigner.Runtime.Tasks.Conditionals; using Opsive.GraphDesigner.Runtime; using Opsive.GraphDesigner.Runtime.Variables; using Opsive.Shared.Utility; using SLSUtilities.General; using UnityEngine; namespace Cielonos.MainGame.Characters.AI { [Description("使用伪随机分布(PRD/稳定概率)进行掷骰判定。连续判定失败会提升下次触发几率,判定成功后重置。用于防脸黑与连爆,制造更为平滑均匀的“伪随机”发生感。")] [NodeIcon("69bf50f8923f54c4c8bb8e258883a411", "6c5770241610a4c4aae4ac3af0ac8bf8")] [Category("Cielonos")] public class StableRandomProbability : Conditional { [Tooltip("当前节点期望判定的成功概率(0~1)。")] public SharedVariable successProbability = 0.25f; [Tooltip("是否启用跨节点共享概率机制。启用后,多个绑定了同一 SharedStableProbability 变量的 StableRandomProbability 节点将共享同一个 N 计数(但可独立设定各自的目标概率 P)。如果不启用,则每个节点独立追踪自己的 N 计数。")] public bool useSharedProbability = false; [Tooltip("【可选】跨节点共享状态槽。如果需要多个不同的判定节点(如:连续3次不同的普攻)共享同一个失败累计次数 N,请绑定同一个 SharedStableProbability 变量")] public SharedVariable sharedStableProbability; [Tooltip("冷却时间(秒)。在此期间内如果节点被每帧重复求值(由于条件打断 Lower Priority/Both),将直接返回上一次的判定结果,不会进行新的掷骰。配置为 0 则不使用冷却。")] public SharedVariable rollCooldown = 0.5f; private float _lastRollTime = -999f; private bool _lastResult = false; private StableProbability _internalProbability; public override void OnAwake() { // 如果用户没有绑定共享变量,或者绑定了但由于某种原因无法引用 if (!useSharedProbability) { // 创建此节点私有的稳定概率对象 _internalProbability = new StableProbability(successProbability.Value); } else { // 如果绑定了共享变量,但在整棵树第一次唤醒时尚未初始化其值实例,则由此节点代为创建 if (sharedStableProbability.Value == null) { sharedStableProbability.Value = new StableProbability(successProbability.Value); } } } public override TaskStatus OnUpdate() { if (rollCooldown != null && rollCooldown.Value > 0f) { if (Time.time - _lastRollTime < rollCooldown.Value) { return _lastResult ? TaskStatus.Success : TaskStatus.Failure; } } StableProbability prdInstance = null; if (useSharedProbability && sharedStableProbability.Value != null) { prdInstance = sharedStableProbability.Value; // 实时更新外接共享实例的期望概率 (防止外部在游戏运行中用其他 Action 更改了目标百分比) prdInstance.Probability = successProbability.Value; } else { prdInstance = _internalProbability; if (prdInstance != null) { prdInstance.Probability = successProbability.Value; } } if (prdInstance == null) { return TaskStatus.Failure; } // 执行 Roll 判定并自动增加 N 计数 / 重置 N 计数 Debug.Log($"当前实际概率: {prdInstance.C * prdInstance.N:F2}"); bool isSuccess = prdInstance.Roll(); _lastResult = isSuccess; _lastRollTime = Time.time; return isSuccess ? TaskStatus.Success : TaskStatus.Failure; } public override void Reset() { base.Reset(); successProbability = 0.25f; rollCooldown = 0.5f; _lastRollTime = -999f; _lastResult = false; useSharedProbability = false; sharedStableProbability = null; if (_internalProbability != null) { _internalProbability.Reset(); } } } }