提交修改
This commit is contained in:
@@ -18628,6 +18628,21 @@ MonoBehaviour:
|
|||||||
- {fileID: 102900000, guid: aa3f5467c0c153642ac320466aee0ec1, type: 3}
|
- {fileID: 102900000, guid: aa3f5467c0c153642ac320466aee0ec1, type: 3}
|
||||||
FilterEnum: 0
|
FilterEnum: 0
|
||||||
Filter: '*'
|
Filter: '*'
|
||||||
|
- Path: Assets/ResRaw/Prefabs/Line/Line1.prefab
|
||||||
|
Address: Plyaer/Line1
|
||||||
|
Type: GameObject
|
||||||
|
Bundle: main/plyaer.bundle
|
||||||
|
Tags:
|
||||||
|
Group:
|
||||||
|
Name: Plyaer
|
||||||
|
Enable: 1
|
||||||
|
BundleMode: 0
|
||||||
|
AddressMode: 2
|
||||||
|
Tags:
|
||||||
|
Collectors:
|
||||||
|
- {fileID: 102900000, guid: aa3f5467c0c153642ac320466aee0ec1, type: 3}
|
||||||
|
FilterEnum: 0
|
||||||
|
Filter: '*'
|
||||||
- Path: Assets/ResRaw/Prefabs/Line/LineHand1.prefab
|
- Path: Assets/ResRaw/Prefabs/Line/LineHand1.prefab
|
||||||
Address: Plyaer/LineHand1
|
Address: Plyaer/LineHand1
|
||||||
Type: GameObject
|
Type: GameObject
|
||||||
|
|||||||
BIN
Assets/New Terrain 14.asset
Normal file
BIN
Assets/New Terrain 14.asset
Normal file
Binary file not shown.
8
Assets/New Terrain 14.asset.meta
Normal file
8
Assets/New Terrain 14.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e5b77e954e13d8644859b63f2252924f
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 15600000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
1127
Assets/ResRaw/Prefabs/Line/Line1.prefab
Normal file
1127
Assets/ResRaw/Prefabs/Line/Line1.prefab
Normal file
File diff suppressed because it is too large
Load Diff
12
Assets/ResRaw/Prefabs/Line/Line1.prefab.meta
Normal file
12
Assets/ResRaw/Prefabs/Line/Line1.prefab.meta
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ea6901d8aa7c41d41987d8ca92b02f6d
|
||||||
|
timeCreated: 1762387921
|
||||||
|
licenseType: Free
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
addedObjectFileIDs:
|
||||||
|
isPrefabVariant: 0
|
||||||
|
variantParentGUID: 00000000000000000000000000000000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1630,8 +1630,8 @@ Camera:
|
|||||||
y: 0
|
y: 0
|
||||||
width: 1
|
width: 1
|
||||||
height: 1
|
height: 1
|
||||||
near clip plane: 0.01
|
near clip plane: 0.1
|
||||||
far clip plane: 5000
|
far clip plane: 3000
|
||||||
field of view: 60.000004
|
field of view: 60.000004
|
||||||
orthographic: 0
|
orthographic: 0
|
||||||
orthographic size: 5
|
orthographic size: 5
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
using Fantasy;
|
using Fantasy;
|
||||||
using Fantasy.Async;
|
using Fantasy.Async;
|
||||||
using Fantasy.Entitas;
|
using Fantasy.Entitas;
|
||||||
using NBF.Fishing2;
|
|
||||||
using RootMotion.FinalIK;
|
|
||||||
using Log = NBC.Log;
|
using Log = NBC.Log;
|
||||||
|
|
||||||
namespace NBF
|
namespace NBF
|
||||||
|
|||||||
3
Assets/Scripts/Fishing/New/View/FishingLine.meta
Normal file
3
Assets/Scripts/Fishing/New/View/FishingLine.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 23b031de32454768b4cd922619ef4e8e
|
||||||
|
timeCreated: 1776227094
|
||||||
@@ -8,13 +8,6 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace NBF
|
namespace NBF
|
||||||
{
|
{
|
||||||
public enum LineType
|
|
||||||
{
|
|
||||||
Hand,
|
|
||||||
HandDouble,
|
|
||||||
Spinning,
|
|
||||||
SpinningFloat,
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FLine : FGearBase
|
public class FLine : FGearBase
|
||||||
{
|
{
|
||||||
@@ -41,6 +34,9 @@ namespace NBF
|
|||||||
|
|
||||||
public float LinelenghtDiferent;
|
public float LinelenghtDiferent;
|
||||||
|
|
||||||
|
|
||||||
|
public float Length { get; private set; }
|
||||||
|
|
||||||
protected override void OnInit()
|
protected override void OnInit()
|
||||||
{
|
{
|
||||||
var tipRb = Rod.Asset.LineConnectorRigidbody;
|
var tipRb = Rod.Asset.LineConnectorRigidbody;
|
||||||
@@ -65,17 +61,6 @@ namespace NBF
|
|||||||
});
|
});
|
||||||
|
|
||||||
StartCoroutine(LureUseGravity());
|
StartCoroutine(LureUseGravity());
|
||||||
if (isLureConnect)
|
|
||||||
{
|
|
||||||
fishingRope.Init(Rod);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fishingRope.Init(Rod);
|
|
||||||
bobberRope.Init(Rod);
|
|
||||||
}
|
|
||||||
|
|
||||||
// rodLine.GenerateLineRendererRope(guides.ToArray(), _LineThickness);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitTest(Rigidbody tipRb)
|
public void InitTest(Rigidbody tipRb)
|
||||||
@@ -101,15 +86,6 @@ namespace NBF
|
|||||||
});
|
});
|
||||||
|
|
||||||
StartCoroutine(LureUseGravity());
|
StartCoroutine(LureUseGravity());
|
||||||
if (isLureConnect)
|
|
||||||
{
|
|
||||||
fishingRope.Init(Rod);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fishingRope.Init(Rod);
|
|
||||||
bobberRope.Init(Rod);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerator LureUseGravity()
|
private IEnumerator LureUseGravity()
|
||||||
@@ -157,7 +133,7 @@ namespace NBF
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return 0;
|
// return 0;
|
||||||
|
|
||||||
//第一个节点到竿稍的位置-第一段鱼线长度
|
//第一个节点到竿稍的位置-第一段鱼线长度
|
||||||
return Vector3.Distance(Bobber.transform.position, Bobber.JointRb.transform.position) -
|
return Vector3.Distance(Bobber.transform.position, Bobber.JointRb.transform.position) -
|
||||||
fishingRope.GetCurrentLength();
|
fishingRope.GetCurrentLength();
|
||||||
@@ -169,5 +145,61 @@ namespace NBF
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region 极限判定
|
||||||
|
|
||||||
|
[Header("Limit Detection")]
|
||||||
|
[Min(0f)]
|
||||||
|
// 极限判定的长度容差,允许链路在总长或单段长度上存在少量误差。
|
||||||
|
[SerializeField]
|
||||||
|
private float lengthLimitTolerance = 0.01f;
|
||||||
|
|
||||||
|
[Min(0f)]
|
||||||
|
// 达到极限后,只有当前超长值大于该阈值时,才开始进入断线候选计时。
|
||||||
|
[SerializeField]
|
||||||
|
private float breakStretchThreshold = 0.05f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当鱼线达到断线条件时发出的一次性消息。
|
||||||
|
/// 外部可订阅该事件,在回调中执行切线、播放表现或状态切换。
|
||||||
|
/// </summary>
|
||||||
|
public event Action<FLine> OnLineBreakRequested;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前断线候选状态的累计时间。
|
||||||
|
/// 只有在处于极限状态,且 CurrentStretchLength 大于断线阈值时才会累加;否则重置为 0。
|
||||||
|
/// </summary>
|
||||||
|
public float LimitStateTime { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前拉力极限百分比。
|
||||||
|
/// 当超长值小于等于 lengthLimitTolerance 时为 0;
|
||||||
|
/// 当超长值大于等于 breakStretchThreshold 时为 100;
|
||||||
|
/// 中间区间按线性比例映射,供 UI 显示使用。
|
||||||
|
/// </summary>
|
||||||
|
public float CurrentBreakStretchPercent => EvaluateBreakStretchPercent(Length);
|
||||||
|
|
||||||
|
|
||||||
|
private float EvaluateBreakStretchPercent(float stretchLength)
|
||||||
|
{
|
||||||
|
if (stretchLength <= lengthLimitTolerance)
|
||||||
|
{
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stretchLength >= breakStretchThreshold)
|
||||||
|
{
|
||||||
|
return 100f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (breakStretchThreshold <= lengthLimitTolerance)
|
||||||
|
{
|
||||||
|
return 100f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Mathf.InverseLerp(lengthLimitTolerance, breakStretchThreshold, stretchLength) * 100f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
3
Assets/Scripts/Fishing/New/View/FishingLine/Feature.meta
Normal file
3
Assets/Scripts/Fishing/New/View/FishingLine/Feature.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: aecb364a5aa3486d9e9c4f37ba801403
|
||||||
|
timeCreated: 1776227164
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NBF
|
||||||
|
{
|
||||||
|
public abstract class FishingLineNodeFeature : MonoBehaviour
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 当前功能组件所属的节点。
|
||||||
|
/// </summary>
|
||||||
|
public FishingLineNode Node { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前功能组件所属的鱼线求解器。
|
||||||
|
/// </summary>
|
||||||
|
public FLine Solver { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将当前功能组件绑定到指定节点和求解器。
|
||||||
|
/// </summary>
|
||||||
|
public void Bind(FishingLineNode node, FLine solver)
|
||||||
|
{
|
||||||
|
Node = node;
|
||||||
|
Solver = solver;
|
||||||
|
|
||||||
|
if (!IsSupportedNode(node))
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"{GetType().Name} 不适用于节点 {node.name} 的当前配置。", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
OnBind();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前功能组件是否支持挂在该节点上。
|
||||||
|
/// 子类可按节点类型、尾节点类型或产品标识做限制。
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool IsSupportedNode(FishingLineNode node)
|
||||||
|
{
|
||||||
|
return node != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点与求解器绑定完成后的回调。
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnBind()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 鱼线链路重建完成后的回调。
|
||||||
|
/// </summary>
|
||||||
|
public virtual void OnLineBuilt()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 鱼线达到断线条件后的回调。
|
||||||
|
/// </summary>
|
||||||
|
public virtual void OnLineBreakRequested()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ad2d8ec3c7054440819bc7a15991f724
|
||||||
|
timeCreated: 1776227197
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NBF
|
||||||
|
{
|
||||||
|
public abstract class FishingLineNodeMotionFeature : FishingLineNodeFeature
|
||||||
|
{
|
||||||
|
[Header("Motion Control")] [SerializeField]
|
||||||
|
private int priorityOffset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前运动控制组件的优先级。
|
||||||
|
/// 值越大,越容易取得节点运动控制权。
|
||||||
|
/// 最终优先级 = 默认优先级 + 调整值。
|
||||||
|
/// </summary>
|
||||||
|
public int Priority => DefaultPriority + priorityOffset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前运动控制组件的默认优先级。
|
||||||
|
/// 子类可通过重写该值,决定自己相对默认物理的抢占能力。
|
||||||
|
/// </summary>
|
||||||
|
protected virtual int DefaultPriority => 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前帧该运动控制组件是否希望接管节点运动。
|
||||||
|
/// </summary>
|
||||||
|
public abstract bool CanControl();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前运动控制组件开始接管节点时的回调。
|
||||||
|
/// </summary>
|
||||||
|
public virtual void OnMotionActivated()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前运动控制组件失去节点控制权时的回调。
|
||||||
|
/// </summary>
|
||||||
|
public virtual void OnMotionDeactivated()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前运动控制组件正在接管节点时,每个 FixedUpdate 执行的逻辑。
|
||||||
|
/// </summary>
|
||||||
|
public abstract void TickMotion(float deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: aabd7c367ac74642942c4a4499e35281
|
||||||
|
timeCreated: 1776227237
|
||||||
253
Assets/Scripts/Fishing/New/View/FishingLine/FishingLineNode.cs
Normal file
253
Assets/Scripts/Fishing/New/View/FishingLine/FishingLineNode.cs
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NBF
|
||||||
|
{
|
||||||
|
public class FishingLineNode : MonoBehaviour
|
||||||
|
{
|
||||||
|
public enum NodeType
|
||||||
|
{
|
||||||
|
Start,
|
||||||
|
Float,
|
||||||
|
Weight,
|
||||||
|
Tail
|
||||||
|
}
|
||||||
|
|
||||||
|
private FLine _solver;
|
||||||
|
|
||||||
|
[Header("Node")] [SerializeField] private NodeType nodeType = NodeType.Tail;
|
||||||
|
[SerializeField] public Rigidbody body;
|
||||||
|
[SerializeField] private Rope _rope;
|
||||||
|
[SerializeField] private MonoBehaviour interaction;
|
||||||
|
private ConfigurableJoint _joint;
|
||||||
|
|
||||||
|
[Header("Segment To Next Logical Node")] [Min(0f)] [SerializeField]
|
||||||
|
private float segmentLengthToNext = 0.5f;
|
||||||
|
|
||||||
|
[SerializeField] private int runtimeChainIndex = -1;
|
||||||
|
|
||||||
|
|
||||||
|
[SerializeField] private List<FishingLineNodeFeature> features = new();
|
||||||
|
[SerializeField] private List<FishingLineNodeMotionFeature> motionFeatures = new();
|
||||||
|
private bool featureCacheReady;
|
||||||
|
[SerializeField] private FishingLineNodeMotionFeature activeMotionFeature;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前正在接管节点运动的组件。
|
||||||
|
/// </summary>
|
||||||
|
public FishingLineNodeMotionFeature ActiveMotionFeature => activeMotionFeature;
|
||||||
|
|
||||||
|
public NodeType Type
|
||||||
|
{
|
||||||
|
get => nodeType;
|
||||||
|
set => nodeType = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rigidbody Body => body;
|
||||||
|
|
||||||
|
public MonoBehaviour Interaction => interaction;
|
||||||
|
public ConfigurableJoint Joint => _joint;
|
||||||
|
|
||||||
|
public int RuntimeChainIndex => runtimeChainIndex;
|
||||||
|
|
||||||
|
public Vector3 Position => transform.position;
|
||||||
|
|
||||||
|
|
||||||
|
private void Reset()
|
||||||
|
{
|
||||||
|
TryGetComponent(out body);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
_solver = GetComponentInParent<FLine>();
|
||||||
|
_joint = GetComponent<ConfigurableJoint>();
|
||||||
|
EnsureFeatureCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
BindFeatures(_solver);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FixedUpdate()
|
||||||
|
{
|
||||||
|
EnsureFeatureCache();
|
||||||
|
UpdateMotionControl(Time.fixedDeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnValidate()
|
||||||
|
{
|
||||||
|
if (body == null)
|
||||||
|
{
|
||||||
|
TryGetComponent(out body);
|
||||||
|
}
|
||||||
|
|
||||||
|
segmentLengthToNext = Mathf.Max(0f, segmentLengthToNext);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Line
|
||||||
|
|
||||||
|
public void SetLenght(float lenght)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Feature
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取节点上的第一个指定类型功能组件。
|
||||||
|
/// </summary>
|
||||||
|
public T GetFeature<T>() where T : FishingLineNodeFeature
|
||||||
|
{
|
||||||
|
EnsureFeatureCache();
|
||||||
|
for (var i = 0; i < features.Count; i++)
|
||||||
|
{
|
||||||
|
if (features[i] is T result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尝试获取节点上的指定类型功能组件。
|
||||||
|
/// </summary>
|
||||||
|
public bool TryGetFeature<T>(out T feature) where T : FishingLineNodeFeature
|
||||||
|
{
|
||||||
|
feature = GetFeature<T>();
|
||||||
|
return feature != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 刷新并重新绑定当前节点上的功能组件。
|
||||||
|
/// </summary>
|
||||||
|
public void BindFeatures(FLine solver)
|
||||||
|
{
|
||||||
|
EnsureFeatureCache();
|
||||||
|
foreach (var t in features)
|
||||||
|
{
|
||||||
|
t.Bind(this, solver);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveMotionFeature(forceRefresh: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通知当前节点上的所有功能组件,鱼线已重建完成。
|
||||||
|
/// </summary>
|
||||||
|
public void NotifyLineBuilt()
|
||||||
|
{
|
||||||
|
EnsureFeatureCache();
|
||||||
|
foreach (var t in features)
|
||||||
|
{
|
||||||
|
t.OnLineBuilt();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveMotionFeature(forceRefresh: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通知当前节点上的所有功能组件,鱼线已经达到断线条件。
|
||||||
|
/// </summary>
|
||||||
|
public void NotifyLineBreakRequested()
|
||||||
|
{
|
||||||
|
EnsureFeatureCache();
|
||||||
|
foreach (var t in features)
|
||||||
|
{
|
||||||
|
t.OnLineBreakRequested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void EnsureFeatureCache()
|
||||||
|
{
|
||||||
|
if (!featureCacheReady)
|
||||||
|
{
|
||||||
|
RefreshFeatures();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshFeatures()
|
||||||
|
{
|
||||||
|
features.Clear();
|
||||||
|
motionFeatures.Clear();
|
||||||
|
GetComponents(features);
|
||||||
|
for (var i = 0; i < features.Count; i++)
|
||||||
|
{
|
||||||
|
if (features[i] is FishingLineNodeMotionFeature motionFeature)
|
||||||
|
{
|
||||||
|
motionFeatures.Add(motionFeature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activeMotionFeature = null;
|
||||||
|
featureCacheReady = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateMotionControl(float deltaTime)
|
||||||
|
{
|
||||||
|
var motionFeature = ResolveMotionFeature(forceRefresh: false);
|
||||||
|
if (motionFeature == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
motionFeature.TickMotion(deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FishingLineNodeMotionFeature ResolveMotionFeature(bool forceRefresh)
|
||||||
|
{
|
||||||
|
EnsureFeatureCache();
|
||||||
|
|
||||||
|
var bestMotionFeature = default(FishingLineNodeMotionFeature);
|
||||||
|
var bestPriority = int.MinValue;
|
||||||
|
|
||||||
|
foreach (var motionFeature in motionFeatures)
|
||||||
|
{
|
||||||
|
var r = !motionFeature.IsSupportedNode(this);
|
||||||
|
var n = !motionFeature.CanControl();
|
||||||
|
if (motionFeature == null || !motionFeature.IsSupportedNode(this) || !motionFeature.CanControl())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestMotionFeature != null && motionFeature.Priority <= bestPriority)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bestMotionFeature = motionFeature;
|
||||||
|
bestPriority = motionFeature.Priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!forceRefresh && ReferenceEquals(activeMotionFeature, bestMotionFeature))
|
||||||
|
{
|
||||||
|
return activeMotionFeature;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeMotionFeature != null && !ReferenceEquals(activeMotionFeature, bestMotionFeature))
|
||||||
|
{
|
||||||
|
activeMotionFeature.OnMotionDeactivated();
|
||||||
|
}
|
||||||
|
|
||||||
|
activeMotionFeature = bestMotionFeature;
|
||||||
|
if (activeMotionFeature != null)
|
||||||
|
{
|
||||||
|
activeMotionFeature.OnMotionActivated();
|
||||||
|
}
|
||||||
|
|
||||||
|
return activeMotionFeature;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private void OnDrawGizmos()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f89affce787d4a1cbcd68bed409183d7
|
||||||
|
timeCreated: 1776227097
|
||||||
272
Assets/Scripts/Fishing/New/View/FishingLine/FishingLineSolver.cs
Normal file
272
Assets/Scripts/Fishing/New/View/FishingLine/FishingLineSolver.cs
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NBF
|
||||||
|
{
|
||||||
|
public enum LineType
|
||||||
|
{
|
||||||
|
Hand,
|
||||||
|
HandDouble,
|
||||||
|
Spinning,
|
||||||
|
SpinningFloat,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FishingLineSolver : FGearBase
|
||||||
|
{
|
||||||
|
[SerializeField] public LineType LineType;
|
||||||
|
|
||||||
|
|
||||||
|
[Header("References")] [SerializeField]
|
||||||
|
private Transform anchorTransform;
|
||||||
|
|
||||||
|
[SerializeField] private FishingLineNode[] logicalNodes = Array.Empty<FishingLineNode>();
|
||||||
|
|
||||||
|
public JointPinchController PinchController;
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
// var tipRb = Rod.Asset.LineConnectorRigidbody;
|
||||||
|
// anchorTransform = tipRb.transform;
|
||||||
|
//
|
||||||
|
// GetComponentsInChildren<Transform>(includeInactive: true).ToList().ForEach(delegate(Transform i)
|
||||||
|
// {
|
||||||
|
// i.gameObject.SetActive(true);
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
GetComponentsInChildren<Transform>(includeInactive: true).ToList().ForEach(delegate(Transform i)
|
||||||
|
{
|
||||||
|
i.gameObject.SetActive(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FixedUpdate()
|
||||||
|
{
|
||||||
|
UpdateAnchorNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Start Node
|
||||||
|
|
||||||
|
private void ConfigureStartNode()
|
||||||
|
{
|
||||||
|
if (logicalNodes == null || logicalNodes.Length == 0 || logicalNodes[0] == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var startNode = logicalNodes[0];
|
||||||
|
startNode.Type = FishingLineNode.NodeType.Start;
|
||||||
|
|
||||||
|
if (startNode.Body != null)
|
||||||
|
{
|
||||||
|
startNode.Body.isKinematic = true;
|
||||||
|
startNode.Body.interpolation = RigidbodyInterpolation.Interpolate;
|
||||||
|
startNode.Body.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateAnchorNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAnchorNode()
|
||||||
|
{
|
||||||
|
if (anchorTransform == null || logicalNodes == null || logicalNodes.Length == 0 || logicalNodes[0] == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var startNode = logicalNodes[0];
|
||||||
|
startNode.transform.SetPositionAndRotation(anchorTransform.position, anchorTransform.rotation);
|
||||||
|
|
||||||
|
if (startNode.Body != null)
|
||||||
|
{
|
||||||
|
if (!startNode.Body.isKinematic)
|
||||||
|
{
|
||||||
|
startNode.Body.linearVelocity = Vector3.zero;
|
||||||
|
startNode.Body.angularVelocity = Vector3.zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Line
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前逻辑链总长度超出配置总长度的部分,小于等于零时记为 0。
|
||||||
|
/// </summary>
|
||||||
|
public float CurrentStretchLength { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置指定逻辑段的配置长度。
|
||||||
|
/// segmentIndex 为 0 时表示第一段;大于 0 时表示对应逻辑节点到下一个逻辑节点的线长。
|
||||||
|
/// </summary>
|
||||||
|
public void SetLenght(float length, int index = 0)
|
||||||
|
{
|
||||||
|
ConfigureStartNode();
|
||||||
|
var node = logicalNodes[index];
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
node.SetLenght(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region LineNode
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前配置的逻辑节点只读列表。
|
||||||
|
/// 外部可读取节点顺序,但不应直接修改数组内容。
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<FishingLineNode> LogicalNodes => logicalNodes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据类型获取逻辑节点类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public FishingLineNode GetLogicalNode(FishingLineNode.NodeType nodeType)
|
||||||
|
{
|
||||||
|
foreach (var fishingLineNode in logicalNodes)
|
||||||
|
{
|
||||||
|
if (fishingLineNode.Type == nodeType)
|
||||||
|
{
|
||||||
|
return fishingLineNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定顺序索引的逻辑节点。
|
||||||
|
/// 索引基于 logicalNodes 配置顺序;超出范围或节点为空时返回 null。
|
||||||
|
/// </summary>
|
||||||
|
public FishingLineNode GetLogicalNode(int logicalIndex)
|
||||||
|
{
|
||||||
|
if (logicalNodes == null || logicalIndex < 0 || logicalIndex >= logicalNodes.Length)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return logicalNodes[logicalIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前起点逻辑节点。
|
||||||
|
/// 会返回配置顺序中第一个非空节点。
|
||||||
|
/// </summary>
|
||||||
|
public FishingLineNode GetStartNode()
|
||||||
|
{
|
||||||
|
return FindFirstValidLogicalNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前终点逻辑节点。
|
||||||
|
/// 会返回配置顺序中最后一个非空节点。
|
||||||
|
/// </summary>
|
||||||
|
public FishingLineNode GetEndNode()
|
||||||
|
{
|
||||||
|
return FindLastValidLogicalNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private FishingLineNode FindFirstValidLogicalNode()
|
||||||
|
{
|
||||||
|
if (logicalNodes == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < logicalNodes.Length; i++)
|
||||||
|
{
|
||||||
|
if (logicalNodes[i] != null)
|
||||||
|
{
|
||||||
|
return logicalNodes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FishingLineNode FindLastValidLogicalNode()
|
||||||
|
{
|
||||||
|
if (logicalNodes == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = logicalNodes.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (logicalNodes[i] != null)
|
||||||
|
{
|
||||||
|
return logicalNodes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 极限判定
|
||||||
|
|
||||||
|
[Header("Limit Detection")]
|
||||||
|
[Min(0f)]
|
||||||
|
// 极限判定的长度容差,允许链路在总长或单段长度上存在少量误差。
|
||||||
|
[SerializeField]
|
||||||
|
private float lengthLimitTolerance = 0.01f;
|
||||||
|
|
||||||
|
[Min(0f)]
|
||||||
|
// 达到极限后,只有当前超长值大于该阈值时,才开始进入断线候选计时。
|
||||||
|
[SerializeField]
|
||||||
|
private float breakStretchThreshold = 0.05f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当鱼线达到断线条件时发出的一次性消息。
|
||||||
|
/// 外部可订阅该事件,在回调中执行切线、播放表现或状态切换。
|
||||||
|
/// </summary>
|
||||||
|
public event Action<FishingLineSolver> OnLineBreakRequested;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前断线候选状态的累计时间。
|
||||||
|
/// 只有在处于极限状态,且 CurrentStretchLength 大于断线阈值时才会累加;否则重置为 0。
|
||||||
|
/// </summary>
|
||||||
|
public float LimitStateTime { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前拉力极限百分比。
|
||||||
|
/// 当超长值小于等于 lengthLimitTolerance 时为 0;
|
||||||
|
/// 当超长值大于等于 breakStretchThreshold 时为 100;
|
||||||
|
/// 中间区间按线性比例映射,供 UI 显示使用。
|
||||||
|
/// </summary>
|
||||||
|
public float CurrentBreakStretchPercent => EvaluateBreakStretchPercent(CurrentStretchLength);
|
||||||
|
|
||||||
|
|
||||||
|
private float EvaluateBreakStretchPercent(float stretchLength)
|
||||||
|
{
|
||||||
|
if (stretchLength <= lengthLimitTolerance)
|
||||||
|
{
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stretchLength >= breakStretchThreshold)
|
||||||
|
{
|
||||||
|
return 100f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (breakStretchThreshold <= lengthLimitTolerance)
|
||||||
|
{
|
||||||
|
return 100f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Mathf.InverseLerp(lengthLimitTolerance, breakStretchThreshold, stretchLength) * 100f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 78dc478e56ff48849761861244c93535
|
||||||
|
timeCreated: 1776227360
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NBF
|
||||||
|
{
|
||||||
|
public class FishingLineTestController : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("References")] [SerializeField]
|
||||||
|
private FishingLineSolver solver;
|
||||||
|
|
||||||
|
[Header("Length Test")] [Min(0f)] [SerializeField]
|
||||||
|
private float initialFirstSegmentLength = 1.2f;
|
||||||
|
|
||||||
|
[Min(0f)] [SerializeField] private float minFirstSegmentLength = 0.1f;
|
||||||
|
[Min(0f)] [SerializeField] private float maxFirstSegmentLength = 5f;
|
||||||
|
[Min(0f)] [SerializeField] private float lineAdjustSpeed = 1f;
|
||||||
|
|
||||||
|
[Header("Input")] [SerializeField] private KeyCode extendKey = KeyCode.UpArrow;
|
||||||
|
[SerializeField] private KeyCode retractKey = KeyCode.DownArrow;
|
||||||
|
|
||||||
|
private float targetFirstSegmentLength;
|
||||||
|
|
||||||
|
private void Reset()
|
||||||
|
{
|
||||||
|
if (solver == null)
|
||||||
|
{
|
||||||
|
solver = GetComponent<FishingLineSolver>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
if (solver == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetFirstSegmentLength =
|
||||||
|
Mathf.Clamp(initialFirstSegmentLength, minFirstSegmentLength, maxFirstSegmentLength);
|
||||||
|
solver.SetLenght(targetFirstSegmentLength);
|
||||||
|
// solver.BuildLine();
|
||||||
|
|
||||||
|
solver.OnLineBreakRequested += OnLineBreakRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLineBreakRequested(FishingLineSolver lineSolver)
|
||||||
|
{
|
||||||
|
Debug.LogError($"当前拉力达到极限,切线,极限时间={lineSolver.LimitStateTime}");
|
||||||
|
var endNode = lineSolver.GetEndNode();
|
||||||
|
if (endNode != null)
|
||||||
|
{
|
||||||
|
endNode.Body.isKinematic = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
if (solver == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var input = 0f;
|
||||||
|
if (Input.GetKey(extendKey))
|
||||||
|
{
|
||||||
|
input += 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Input.GetKey(retractKey))
|
||||||
|
{
|
||||||
|
input -= 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Mathf.Approximately(input, 0f))
|
||||||
|
{
|
||||||
|
targetFirstSegmentLength += input * lineAdjustSpeed * Time.deltaTime;
|
||||||
|
targetFirstSegmentLength =
|
||||||
|
Mathf.Clamp(targetFirstSegmentLength, minFirstSegmentLength, maxFirstSegmentLength);
|
||||||
|
solver.SetLenght(targetFirstSegmentLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (solver.CurrentBreakStretchPercent > 0)
|
||||||
|
{
|
||||||
|
// Debug.LogError(solver.CurrentBreakStretchPercent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5382d66f55f6463cb469c5094b0e7a6b
|
||||||
|
timeCreated: 1776234046
|
||||||
@@ -178,14 +178,7 @@ public class Rope : MonoBehaviour
|
|||||||
private bool _isCulledByVisibility;
|
private bool _isCulledByVisibility;
|
||||||
private int _tIdleSubdiv = -1;
|
private int _tIdleSubdiv = -1;
|
||||||
private int _tMovingSubdiv = -1;
|
private int _tMovingSubdiv = -1;
|
||||||
|
|
||||||
private FRod _rod;
|
|
||||||
public void Init(FRod rod)
|
|
||||||
{
|
|
||||||
_rod = rod;
|
|
||||||
if (Application.isPlaying)
|
|
||||||
RefreshVisibilityState(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Catmull t caches(只缓存 idle/moving 两档,减少每帧重复乘法)
|
// Catmull t caches(只缓存 idle/moving 两档,减少每帧重复乘法)
|
||||||
private struct TCaches
|
private struct TCaches
|
||||||
@@ -250,15 +243,7 @@ public class Rope : MonoBehaviour
|
|||||||
_startTr = startAnchor ? startAnchor.transform : null;
|
_startTr = startAnchor ? startAnchor.transform : null;
|
||||||
_endTr = endAnchor ? endAnchor.transform : null;
|
_endTr = endAnchor ? endAnchor.transform : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ShouldAlwaysSimulate()
|
|
||||||
{
|
|
||||||
if (!localOwnerAlwaysSimulate)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var owner = _rod?.PlayerItem?.Owner;
|
|
||||||
return owner == null || owner.IsSelf;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Transform GetActiveCameraTransform()
|
private Transform GetActiveCameraTransform()
|
||||||
{
|
{
|
||||||
@@ -312,7 +297,7 @@ public class Rope : MonoBehaviour
|
|||||||
|
|
||||||
private void RefreshVisibilityState(bool force = false)
|
private void RefreshVisibilityState(bool force = false)
|
||||||
{
|
{
|
||||||
if (!cullRemoteRopeWhenInvisible || ShouldAlwaysSimulate())
|
if (!cullRemoteRopeWhenInvisible)
|
||||||
{
|
{
|
||||||
_isCulledByVisibility = false;
|
_isCulledByVisibility = false;
|
||||||
if (_lineRenderer)
|
if (_lineRenderer)
|
||||||
|
|||||||
@@ -24,25 +24,25 @@ EditorUserSettings:
|
|||||||
value: 0005505f515750595e5f5f23412507441216497f2d7f24367e711c64b6b86c61
|
value: 0005505f515750595e5f5f23412507441216497f2d7f24367e711c64b6b86c61
|
||||||
flags: 0
|
flags: 0
|
||||||
RecentlyUsedSceneGuid-3:
|
RecentlyUsedSceneGuid-3:
|
||||||
value: 5309035757065a0a54575f7216265c4444151d28792e72627d2f1935bbb8673a
|
|
||||||
flags: 0
|
|
||||||
RecentlyUsedSceneGuid-4:
|
|
||||||
value: 00050c5150005f5f54560f2640270d4410161c28282b72357e7c4835e4b63760
|
|
||||||
flags: 0
|
|
||||||
RecentlyUsedSceneGuid-5:
|
|
||||||
value: 06090c5f54015f5a0f085b7b11765d444e4e1e287429773178704561b3b23561
|
|
||||||
flags: 0
|
|
||||||
RecentlyUsedSceneGuid-6:
|
|
||||||
value: 0257035f51050d090f0f5d734521094414164e797e7a20667d7a4536e0e36461
|
|
||||||
flags: 0
|
|
||||||
RecentlyUsedSceneGuid-7:
|
|
||||||
value: 54070c5452075002590c0871127b5a4443161c2f797176312c2f1e6bb1b4353d
|
value: 54070c5452075002590c0871127b5a4443161c2f797176312c2f1e6bb1b4353d
|
||||||
flags: 0
|
flags: 0
|
||||||
|
RecentlyUsedSceneGuid-4:
|
||||||
|
value: 5309035757065a0a54575f7216265c4444151d28792e72627d2f1935bbb8673a
|
||||||
|
flags: 0
|
||||||
|
RecentlyUsedSceneGuid-5:
|
||||||
|
value: 00050c5150005f5f54560f2640270d4410161c28282b72357e7c4835e4b63760
|
||||||
|
flags: 0
|
||||||
|
RecentlyUsedSceneGuid-6:
|
||||||
|
value: 06090c5f54015f5a0f085b7b11765d444e4e1e287429773178704561b3b23561
|
||||||
|
flags: 0
|
||||||
|
RecentlyUsedSceneGuid-7:
|
||||||
|
value: 0257035f51050d090f0f5d734521094414164e797e7a20667d7a4536e0e36461
|
||||||
|
flags: 0
|
||||||
RecentlyUsedSceneGuid-8:
|
RecentlyUsedSceneGuid-8:
|
||||||
value: 07060c5454040c0a545b547240700a441216417e7f2e7268752c4966b4b0663d
|
value: 5505015f5c515a085f5b092149760f441716407a787d7564287b1b36e7e1366e
|
||||||
flags: 0
|
flags: 0
|
||||||
RecentlyUsedSceneGuid-9:
|
RecentlyUsedSceneGuid-9:
|
||||||
value: 5505015f5c515a085f5b092149760f441716407a787d7564287b1b36e7e1366e
|
value: 07060c5454040c0a545b547240700a441216417e7f2e7268752c4966b4b0663d
|
||||||
flags: 0
|
flags: 0
|
||||||
UnityEditor.ShaderGraph.Blackboard:
|
UnityEditor.ShaderGraph.Blackboard:
|
||||||
value: 18135939215a0a5004000b0e15254b524c030a3f2964643d120d1230e9e93a3fd6e826abbd2e2d293c4ead313b08042de6030a0afa240c0d020be94c4ba75e435d8715fa32c70d15d11612dacc11fee5d3c5d1fe9ab1bf968e93e2ffcbc3e7e2f0b3ffe0e8b0be9afeffa9ffff8e85dd8390e2969e8899daa7
|
value: 18135939215a0a5004000b0e15254b524c030a3f2964643d120d1230e9e93a3fd6e826abbd2e2d293c4ead313b08042de6030a0afa240c0d020be94c4ba75e435d8715fa32c70d15d11612dacc11fee5d3c5d1fe9ab1bf968e93e2ffcbc3e7e2f0b3ffe0e8b0be9afeffa9ffff8e85dd8390e2969e8899daa7
|
||||||
|
|||||||
Reference in New Issue
Block a user