Initial Unity project commit

This commit is contained in:
2026-07-08 19:53:15 +08:00
commit 75f254212e
15657 changed files with 11633879 additions and 0 deletions
@@ -0,0 +1,11 @@
using UnityEngine;
namespace Interaction.Conditions
{
public interface IInteractCondition
{
bool CanInteract(GameObject interactor, out string failReason);
void OnInteractSucceeded(GameObject interactor);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 276a36eb0f3667446bb5302f67108f9f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
using UnityEngine;
namespace Interaction.Conditions
{
public abstract class InteractConditionBehaviour : MonoBehaviour, IInteractCondition
{
public abstract bool CanInteract(GameObject interactor, out string failReason);
public abstract void OnInteractSucceeded(GameObject interactor);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1d8be21aac1d2e84abce8f6482630586
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,40 @@
using UnityEngine;
namespace Interaction.Conditions
{
public class InteractCountAccumulator : InteractConditionBehaviour
{
[SerializeField] private Interaction.InteractCountThresholdSystem targetSystem;
[SerializeField] private int addAmount = 1;
[SerializeField] private bool autoFindTargetSystemIfNull = true;
private bool hadTargetSystemAtEnable = false;
private void OnEnable()
{
hadTargetSystemAtEnable = targetSystem != null;
}
public override bool CanInteract(GameObject interactor, out string failReason)
{
failReason = null;
return true;
}
public override void OnInteractSucceeded(GameObject interactor)
{
if (addAmount <= 0) return;
if (targetSystem == null)
{
if (hadTargetSystemAtEnable) return;
if (!autoFindTargetSystemIfNull) return;
targetSystem = FindObjectOfType<Interaction.InteractCountThresholdSystem>(true);
}
if (targetSystem == null) return;
targetSystem.Add(addAmount);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bf38ecf9d6a2b4e4db75e90f1e7cebaa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,250 @@
using System;
using Inventory;
using UnityEngine;
namespace Interaction.Conditions
{
public class RequireItemsInteractCondition : InteractConditionBehaviour
{
public enum RequireMode
{
RequireAll = 0,
RequireAny = 1
}
[Serializable]
private struct Requirement
{
public string requiredItemId;
[Min(1)] public int requiredAmount;
}
[Header("需求")]
[SerializeField] private RequireMode mode = RequireMode.RequireAll;
[SerializeField] private Requirement[] requirements;
[Header("成功后处理")]
[SerializeField] private bool consumeOnSuccess;
[SerializeField] private bool unlockAfterSuccess;
[SerializeField] private bool unlocked;
[SerializeField] private InteractUnlockState unlockState;
[Header("失败提示")]
[SerializeField] private string failReason;
[Header("容错")]
[SerializeField] private bool allowIfInventoryMissing;
[Header("Debug")]
[SerializeField] private bool debugLog;
private int lastMatchedIndex = -1;
public override bool CanInteract(GameObject interactor, out string reason)
{
reason = null;
lastMatchedIndex = -1;
if (debugLog)
{
Debug.Log(
$"[RequireItemsInteractCondition] CanInteract on '{name}' " +
$"mode={mode} consumeOnSuccess={consumeOnSuccess} unlockAfterSuccess={unlockAfterSuccess} unlocked={GetUnlocked()} " +
$"requirementsCount={(requirements == null ? 0 : requirements.Length)}",
this);
}
if (unlockAfterSuccess && GetUnlocked())
{
if (debugLog) Debug.Log("[RequireItemsInteractCondition] Bypass: unlocked=true", this);
return true;
}
if (requirements == null || requirements.Length <= 0)
{
if (debugLog) Debug.Log("[RequireItemsInteractCondition] Bypass: requirements empty", this);
return true;
}
var inventory = InventorySystem.Instance;
if (inventory == null)
{
if (allowIfInventoryMissing) return true;
reason = string.IsNullOrWhiteSpace(failReason) ? "缺少背包系统" : failReason;
if (debugLog) Debug.Log($"[RequireItemsInteractCondition] Blocked: inventory missing, reason='{reason}'", this);
return false;
}
if (mode == RequireMode.RequireAny)
{
for (int i = 0; i < requirements.Length; i++)
{
var req = requirements[i];
if (string.IsNullOrWhiteSpace(req.requiredItemId)) continue;
int amount = req.requiredAmount <= 0 ? 1 : req.requiredAmount;
int count = CountItemsById(inventory, req.requiredItemId);
if (debugLog) Debug.Log($"[RequireItemsInteractCondition] Check(Any) id='{req.requiredItemId}' need={amount} have={count}", this);
if (count >= amount)
{
lastMatchedIndex = i;
if (debugLog) Debug.Log($"[RequireItemsInteractCondition] Passed(Any) matchedIndex={lastMatchedIndex}", this);
return true;
}
}
reason = BuildFailReason();
if (debugLog) Debug.Log($"[RequireItemsInteractCondition] Blocked(Any) reason='{reason}'", this);
return false;
}
for (int i = 0; i < requirements.Length; i++)
{
var req = requirements[i];
if (string.IsNullOrWhiteSpace(req.requiredItemId)) continue;
int amount = req.requiredAmount <= 0 ? 1 : req.requiredAmount;
int count = CountItemsById(inventory, req.requiredItemId);
if (debugLog) Debug.Log($"[RequireItemsInteractCondition] Check(All) id='{req.requiredItemId}' need={amount} have={count}", this);
if (count < amount)
{
reason = BuildFailReason();
if (debugLog) Debug.Log($"[RequireItemsInteractCondition] Blocked(All) reason='{reason}'", this);
return false;
}
}
if (debugLog) Debug.Log("[RequireItemsInteractCondition] Passed(All)", this);
return true;
}
public override void OnInteractSucceeded(GameObject interactor)
{
if (unlockAfterSuccess && GetUnlocked()) return;
if (consumeOnSuccess)
{
var inventory = InventorySystem.Instance;
if (inventory != null)
{
if (debugLog) Debug.Log($"[RequireItemsInteractCondition] OnInteractSucceeded: consuming, mode={mode}, matchedIndex={lastMatchedIndex}", this);
Consume(inventory);
}
}
if (unlockAfterSuccess) SetUnlocked(true);
if (debugLog) Debug.Log($"[RequireItemsInteractCondition] OnInteractSucceeded: done, unlocked={GetUnlocked()}", this);
}
private bool GetUnlocked()
{
if (unlockState != null) return unlockState.IsUnlocked;
return unlocked;
}
private void SetUnlocked(bool value)
{
if (unlockState != null)
{
unlockState.SetUnlocked(value);
return;
}
unlocked = value;
}
private void Consume(InventorySystem inventory)
{
if (requirements == null || requirements.Length <= 0) return;
if (mode == RequireMode.RequireAny)
{
if (lastMatchedIndex < 0 || lastMatchedIndex >= requirements.Length) return;
var req = requirements[lastMatchedIndex];
if (string.IsNullOrWhiteSpace(req.requiredItemId)) return;
int amount = req.requiredAmount <= 0 ? 1 : req.requiredAmount;
ConsumeItemsById(inventory, req.requiredItemId, amount);
return;
}
for (int i = 0; i < requirements.Length; i++)
{
var req = requirements[i];
if (string.IsNullOrWhiteSpace(req.requiredItemId)) continue;
int amount = req.requiredAmount <= 0 ? 1 : req.requiredAmount;
ConsumeItemsById(inventory, req.requiredItemId, amount);
}
}
private string BuildFailReason()
{
if (!string.IsNullOrWhiteSpace(failReason)) return failReason;
if (requirements == null || requirements.Length <= 0) return "需要特定道具";
if (mode == RequireMode.RequireAny)
{
return $"需要任意一种物品: {BuildRequirementsList()}";
}
return $"需要全部物品: {BuildRequirementsList()}";
}
private string BuildRequirementsList()
{
string result = null;
for (int i = 0; i < requirements.Length; i++)
{
var req = requirements[i];
if (string.IsNullOrWhiteSpace(req.requiredItemId)) continue;
int amount = req.requiredAmount <= 0 ? 1 : req.requiredAmount;
string token = amount == 1 ? req.requiredItemId : $"{req.requiredItemId}x{amount}";
result = string.IsNullOrEmpty(result) ? token : $"{result}, {token}";
}
return string.IsNullOrEmpty(result) ? "(未配置)" : result;
}
private static int CountItemsById(InventorySystem inventory, string itemId)
{
int total = 0;
var list = inventory.inventory;
if (list == null) return 0;
for (int i = 0; i < list.Count; i++)
{
var entry = list[i];
if (entry?.data == null) continue;
if (entry.data.id != itemId) continue;
total += entry.stackSize;
}
return total;
}
private static void ConsumeItemsById(InventorySystem inventory, string itemId, int amount)
{
int remaining = amount;
var list = inventory.inventory;
if (list == null) return;
for (int i = 0; i < list.Count; i++)
{
if (remaining <= 0) break;
var entry = list[i];
if (entry?.data == null) continue;
if (entry.data.id != itemId) continue;
int take = Mathf.Min(entry.stackSize, remaining);
if (take <= 0) continue;
inventory.RemoveFromSlot(i, take);
remaining -= take;
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0b8090c7c4e156d4da8e0c291573d6d5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,38 @@
using UnityEngine;
namespace Interaction.Conditions
{
public class RequireUnlockedInteractCondition : InteractConditionBehaviour
{
[SerializeField] private InteractUnlockState unlockState;
[Header("失败提示")]
[SerializeField] private string failReason = "尚未解锁";
[Header("容错")]
[SerializeField] private bool allowIfUnlockStateMissing;
public override bool CanInteract(GameObject interactor, out string reason)
{
reason = null;
var state = unlockState != null ? unlockState : GetComponentInParent<InteractUnlockState>();
if (state == null)
{
if (allowIfUnlockStateMissing) return true;
reason = string.IsNullOrWhiteSpace(failReason) ? "尚未解锁" : failReason;
return false;
}
if (state.IsUnlocked) return true;
reason = string.IsNullOrWhiteSpace(failReason) ? "尚未解锁" : failReason;
return false;
}
public override void OnInteractSucceeded(GameObject interactor)
{
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 710fa35292f9cb441828c7da5df3d633
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,56 @@
using UnityEngine;
namespace Interaction.Conditions
{
public class UnlockStatesOnInteractSucceeded : InteractConditionBehaviour
{
[SerializeField] private InteractUnlockState selfState;
[SerializeField] private bool unlockSelf = true;
[SerializeField] private InteractUnlockState[] targetStates;
[Header("Behavior")]
[SerializeField] private bool onlyOnce = true;
[SerializeField] private bool triggered;
[Header("Debug")]
[SerializeField] private bool debugLog;
public override bool CanInteract(GameObject interactor, out string failReason)
{
failReason = null;
return true;
}
public override void OnInteractSucceeded(GameObject interactor)
{
if (onlyOnce && triggered) return;
if (unlockSelf)
{
var state = selfState != null ? selfState : GetComponentInParent<InteractUnlockState>();
if (state != null) state.SetUnlocked(true);
}
if (targetStates != null)
{
for (int i = 0; i < targetStates.Length; i++)
{
var state = targetStates[i];
if (state == null) continue;
state.SetUnlocked(true);
}
}
triggered = true;
if (debugLog)
{
Debug.Log(
$"[UnlockStatesOnInteractSucceeded] Triggered on '{name}' " +
$"unlockSelf={unlockSelf} targets={(targetStates == null ? 0 : targetStates.Length)}",
this);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d572c93dda995242bd2be40cfecc54c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: