Initial Unity project commit
This commit is contained in:
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d572c93dda995242bd2be40cfecc54c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user