2025-10-22 17:34:58 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using Script.Gameplay.Connect;
|
2025-10-27 10:31:01 +08:00
|
|
|
|
using Script.Gameplay.Global;
|
2025-10-22 17:34:58 +08:00
|
|
|
|
using Script.Gameplay.Interface;
|
|
|
|
|
|
using UnityEngine;
|
2025-10-28 09:03:48 +08:00
|
|
|
|
using UnityEngine.Experimental.GlobalIllumination;
|
2025-10-22 17:34:58 +08:00
|
|
|
|
|
|
|
|
|
|
namespace Script.Gameplay.Facility
|
|
|
|
|
|
{
|
|
|
|
|
|
public class BaseFacilityController : MonoBehaviour, IInteractable, IEditableComponent, IConnectable, ISignalSender,
|
|
|
|
|
|
ISignalReceiver, IGaze
|
|
|
|
|
|
{
|
|
|
|
|
|
[SerializeField] protected bool isEnableInteract = true;
|
|
|
|
|
|
[SerializeField] protected bool isEnableEdit = true;
|
|
|
|
|
|
[SerializeField] protected bool isEnableConnect = true;
|
|
|
|
|
|
[SerializeField] protected bool isOpenInEditor = true;
|
2025-10-26 22:13:15 +08:00
|
|
|
|
[SerializeField] protected int needSignalCount = 1;
|
|
|
|
|
|
[SerializeField] protected int currentSignalCount = 0;
|
|
|
|
|
|
[SerializeField] protected string componentName;
|
2025-10-27 10:31:01 +08:00
|
|
|
|
[SerializeField] protected bool isEnableSendSignal = false;
|
2025-10-30 10:03:58 +08:00
|
|
|
|
[SerializeField] protected bool isEnableDeliverSignal = false;
|
2025-10-22 17:34:58 +08:00
|
|
|
|
|
|
|
|
|
|
public virtual bool IsEnableInteract
|
|
|
|
|
|
{
|
|
|
|
|
|
get => isEnableInteract;
|
|
|
|
|
|
set => isEnableInteract = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual bool IsEnableEdit
|
|
|
|
|
|
{
|
|
|
|
|
|
get => isEnableEdit;
|
|
|
|
|
|
set => isEnableEdit = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual bool IsOpenInEditor
|
|
|
|
|
|
{
|
|
|
|
|
|
get => isOpenInEditor;
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
isOpenInEditor = value;
|
|
|
|
|
|
IsEnableInteract = value;
|
|
|
|
|
|
IsEnableConnect = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual bool IsEnableConnect
|
|
|
|
|
|
{
|
|
|
|
|
|
get => isEnableConnect;
|
|
|
|
|
|
set => isEnableConnect = value;
|
|
|
|
|
|
}
|
2025-10-27 10:31:01 +08:00
|
|
|
|
|
2025-10-26 22:13:15 +08:00
|
|
|
|
public virtual int NeedSignalCount
|
|
|
|
|
|
{
|
|
|
|
|
|
get => needSignalCount;
|
|
|
|
|
|
set => needSignalCount = value;
|
|
|
|
|
|
}
|
2025-10-27 10:31:01 +08:00
|
|
|
|
|
2025-10-26 22:13:15 +08:00
|
|
|
|
public virtual int CurrentNeedSignalCount
|
|
|
|
|
|
{
|
|
|
|
|
|
get => currentSignalCount;
|
|
|
|
|
|
set => currentSignalCount = value;
|
|
|
|
|
|
}
|
2025-10-22 17:34:58 +08:00
|
|
|
|
|
|
|
|
|
|
public string ComponentName
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
// 如果没有自定义名称,则使用组件名称
|
|
|
|
|
|
return string.IsNullOrEmpty(componentName) ? GetType().Name : componentName;
|
|
|
|
|
|
}
|
|
|
|
|
|
set => componentName = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public List<ConnectionLine> ConnectionLines { get; set; } = new List<ConnectionLine>();
|
|
|
|
|
|
|
|
|
|
|
|
public string GetConnectableName()
|
|
|
|
|
|
{
|
|
|
|
|
|
return gameObject.name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public GameObject GetGameObject()
|
|
|
|
|
|
{
|
|
|
|
|
|
return gameObject;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual string GetInteractPrompt()
|
|
|
|
|
|
{
|
|
|
|
|
|
return $"Interact with {componentName}";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public Vector3 GetPosition()
|
|
|
|
|
|
{
|
|
|
|
|
|
return transform.position;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual void Interact(GameObject interactor)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Implement interaction logic here
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual void OnGazeEnter(GameObject go)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Handle gaze enter
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual void OnGazeExit(GameObject go)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Handle gaze exit
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual void OnSignalReceived(bool active, GameObject sender)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (active)
|
|
|
|
|
|
{
|
2025-10-26 22:13:15 +08:00
|
|
|
|
CurrentNeedSignalCount++;
|
2025-10-22 17:34:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-10-27 10:31:01 +08:00
|
|
|
|
CurrentNeedSignalCount = Mathf.Max(0, CurrentNeedSignalCount - 1);
|
2025-10-22 17:34:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 22:13:15 +08:00
|
|
|
|
if (CurrentNeedSignalCount < NeedSignalCount)
|
2025-10-22 17:34:58 +08:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-10-30 10:03:58 +08:00
|
|
|
|
|
|
|
|
|
|
if (isEnableDeliverSignal)
|
|
|
|
|
|
{
|
|
|
|
|
|
SendSignal(active, sender);
|
|
|
|
|
|
}
|
2025-10-22 17:34:58 +08:00
|
|
|
|
// Implement signal received logic here
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-27 10:31:01 +08:00
|
|
|
|
public bool IsEnableSendSignal
|
|
|
|
|
|
{
|
|
|
|
|
|
get => isEnableSendSignal;
|
|
|
|
|
|
set => isEnableSendSignal = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-28 08:50:06 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
///
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="active">发送true or false</param>
|
|
|
|
|
|
/// <param name="sender">让此物体触发发送的来源者</param>
|
2025-10-22 17:34:58 +08:00
|
|
|
|
public virtual void SendSignal(bool active, GameObject sender)
|
|
|
|
|
|
{
|
2025-10-28 09:03:48 +08:00
|
|
|
|
if (!IsEnableSendSignal) return;
|
2025-10-22 17:34:58 +08:00
|
|
|
|
if (ConnectionLines != null)
|
|
|
|
|
|
{
|
2025-10-28 09:03:48 +08:00
|
|
|
|
// 利用DFS检测是否存在连接环
|
|
|
|
|
|
if (DfsCheckConnectionRing(this.gameObject))
|
|
|
|
|
|
{
|
2025-10-28 10:10:41 +08:00
|
|
|
|
BUGManager.Instance.LogStackOverflowBug(this.transform);
|
2025-10-28 09:03:48 +08:00
|
|
|
|
StartCoroutine(GameManager.Instance.ReStartGame());
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-10-27 10:31:01 +08:00
|
|
|
|
|
2025-10-22 17:34:58 +08:00
|
|
|
|
foreach (var line in ConnectionLines)
|
|
|
|
|
|
{
|
2025-10-28 08:50:06 +08:00
|
|
|
|
// 排除掉为sender的连接线
|
|
|
|
|
|
if (line.gameObject == sender)
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2025-10-28 09:03:48 +08:00
|
|
|
|
|
2025-10-22 17:34:58 +08:00
|
|
|
|
line.OnSignalReceived(active, this.gameObject);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-28 09:03:48 +08:00
|
|
|
|
|
|
|
|
|
|
public static bool DfsCheckConnectionRing(GameObject root)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (root == null) return false;
|
|
|
|
|
|
|
|
|
|
|
|
var rootConnect = root.GetComponent<IConnectable>();
|
|
|
|
|
|
if (rootConnect == null) return false;
|
|
|
|
|
|
|
|
|
|
|
|
var visited = new HashSet<GameObject>();
|
|
|
|
|
|
|
|
|
|
|
|
// 内部递归 DFS,parent 用于避免把返回到父节点误判为环
|
|
|
|
|
|
bool Dfs(GameObject current, GameObject parent)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (current == null) return false;
|
|
|
|
|
|
visited.Add(current);
|
|
|
|
|
|
|
|
|
|
|
|
var conn = current.GetComponent<IConnectable>();
|
|
|
|
|
|
if (conn == null) return false;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var line in conn.ConnectionLines)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (line == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
GameObject a = line.PointA?.GetGameObject();
|
|
|
|
|
|
GameObject b = line.PointB?.GetGameObject();
|
|
|
|
|
|
|
|
|
|
|
|
GameObject neighbor = null;
|
|
|
|
|
|
if (a == current) neighbor = b;
|
|
|
|
|
|
else if (b == current) neighbor = a;
|
|
|
|
|
|
else continue; // 如果这条线并未连接到 current,跳过
|
|
|
|
|
|
|
|
|
|
|
|
if (neighbor == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
// 如果邻居是父节点,跳过(因为这是无向边回到父)
|
|
|
|
|
|
if (neighbor == parent) continue;
|
|
|
|
|
|
|
|
|
|
|
|
// 已访问到其他非父节点,说明存在环
|
|
|
|
|
|
if (visited.Contains(neighbor))
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (Dfs(neighbor, current))
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Dfs(root, null);
|
|
|
|
|
|
}
|
2025-10-22 17:34:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|