多轮审查评估
This commit is contained in:
@@ -23,7 +23,8 @@ namespace BaseGames.World
|
||||
[SerializeField] private float _bounceForce = 5f;
|
||||
|
||||
[Header("事件频道")]
|
||||
[SerializeField] private StringEventChannelSO _onCollectiblePickup;
|
||||
[SerializeField] private StringEventChannelSO _onCollectiblePickup; // 道具获取(EVT_ItemPickup)
|
||||
[SerializeField] private StringEventChannelSO _onCollectibleSaved; // 持久化记录(EVT_CollectibleSaved)
|
||||
|
||||
private bool _collected;
|
||||
|
||||
@@ -64,7 +65,7 @@ namespace BaseGames.World
|
||||
}
|
||||
|
||||
if (_isPersistent && !string.IsNullOrEmpty(_collectibleId))
|
||||
_onCollectiblePickup?.Raise(_collectibleId);
|
||||
_onCollectibleSaved?.Raise(_collectibleId);
|
||||
|
||||
Despawn();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using BaseGames.Combat;
|
||||
using BaseGames.Core;
|
||||
using BaseGames.Core.Save;
|
||||
using MoreMountains.Feedbacks;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -9,7 +11,7 @@ namespace BaseGames.World
|
||||
/// 揭示后禁用碰撞体(不销毁),状态持久化到 WorldSaveData。
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Collider2D))]
|
||||
public class FalseWall : MonoBehaviour, IDamageable
|
||||
public class FalseWall : MonoBehaviour, IDamageable, ISaveable
|
||||
{
|
||||
public enum RevealCondition { Proximity, AttackOnce, AlwaysOpen }
|
||||
|
||||
@@ -39,17 +41,39 @@ namespace BaseGames.World
|
||||
|
||||
// ── Unity Lifecycle ───────────────────────────────────────────────────
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Register(this);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Unregister(this);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_revealCondition == RevealCondition.AlwaysOpen)
|
||||
{
|
||||
SetPassThroughImmediate();
|
||||
_isRevealed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 读档恢复(SaveManager 集成后接入 WorldSaveData.RevealedFalseWalls)
|
||||
// 示例:bool revealed = ServiceLocator.GetOrDefault<SaveManager>()?.CurrentSave?.World?.RevealedFalseWalls?.Contains(_wallId) ?? false;
|
||||
// if (revealed) SetPassThroughImmediate();
|
||||
// ── ISaveable ─────────────────────────────────────────────────────────
|
||||
public void OnSave(SaveData data)
|
||||
{
|
||||
if (_isRevealed && !string.IsNullOrEmpty(_wallId)
|
||||
&& !data.World.OpenedDoors.Contains(_wallId))
|
||||
data.World.OpenedDoors.Add(_wallId);
|
||||
}
|
||||
|
||||
public void OnLoad(SaveData data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_wallId)) return;
|
||||
_isRevealed = data.World.OpenedDoors.Contains(_wallId);
|
||||
if (_isRevealed) SetPassThroughImmediate();
|
||||
}
|
||||
|
||||
private void OnTriggerEnter2D(Collider2D other)
|
||||
|
||||
@@ -9,13 +9,8 @@ namespace BaseGames.World
|
||||
[RequireComponent(typeof(Collider2D))]
|
||||
public class HazardZone : MonoBehaviour
|
||||
{
|
||||
public enum RespawnType { AtLastSavePoint, AtRoomEntry }
|
||||
|
||||
[SerializeField] private bool _isInstantKill = true;
|
||||
[SerializeField] private int _damage = 9999;
|
||||
#pragma warning disable CS0414
|
||||
[SerializeField] private RespawnType _respawnType = RespawnType.AtLastSavePoint;
|
||||
#pragma warning restore CS0414
|
||||
[SerializeField] private bool _isInstantKill = true;
|
||||
[SerializeField] private int _damage = 9999;
|
||||
|
||||
private void OnTriggerEnter2D(Collider2D other)
|
||||
{
|
||||
|
||||
@@ -18,11 +18,7 @@ namespace BaseGames.World.Liquid
|
||||
[Header("区域标识(存档/跨系统识别)")]
|
||||
[SerializeField] private string _zoneId;
|
||||
|
||||
[Header("伤害(Water 类型专用;Acid/Lava 由子节点 HazardZone 处理)")]
|
||||
#pragma warning disable CS0414
|
||||
[SerializeField] private bool _dealsDrowningDamage = false;
|
||||
[SerializeField] private float _drowningDamagePerSecond = 5f;
|
||||
#pragma warning restore CS0414
|
||||
[Header("伤害(Acid/Lava 由子节点 HazardZone 处理;Water 类型溺死由 WaterDangerState 处理)")]
|
||||
|
||||
[Header("物理配置")]
|
||||
[SerializeField] private LiquidPhysicsConfigSO _physicsConfig;
|
||||
|
||||
@@ -31,7 +31,11 @@ namespace BaseGames.World.Liquid
|
||||
BlendVolume(1f, _blendInDuration);
|
||||
}
|
||||
|
||||
private void OnLiquidExited(LiquidEvent evt) => BlendVolume(0f, _blendOutDuration);
|
||||
private void OnLiquidExited(LiquidEvent evt)
|
||||
{
|
||||
if (evt.LiquidType != LiquidType.Water) return;
|
||||
BlendVolume(0f, _blendOutDuration);
|
||||
}
|
||||
|
||||
private void BlendVolume(float target, float duration)
|
||||
{
|
||||
|
||||
@@ -7,10 +7,14 @@ namespace BaseGames.World
|
||||
/// </summary>
|
||||
public class PhantomInteractable : DirectionalInteractable
|
||||
{
|
||||
private int _phantomBodyLayer;
|
||||
|
||||
private void Awake() => _phantomBodyLayer = LayerMask.NameToLayer("PhantomBody");
|
||||
|
||||
private void OnTriggerEnter2D(Collider2D other)
|
||||
{
|
||||
bool isPlayer = other.CompareTag("Player");
|
||||
bool isPhantom = other.gameObject.layer == LayerMask.NameToLayer("PhantomBody");
|
||||
bool isPhantom = other.gameObject.layer == _phantomBodyLayer;
|
||||
|
||||
if (!isPlayer && !isPhantom) return;
|
||||
TryActivate();
|
||||
|
||||
@@ -23,9 +23,16 @@ namespace BaseGames.Puzzle
|
||||
private bool _isActivated;
|
||||
public bool IsActivated => _isActivated;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
bool savedState = !string.IsNullOrEmpty(_receiverId)
|
||||
&& _worldState != null
|
||||
&& _worldState.HasFlag("receiver_" + _receiverId);
|
||||
_isActivated = savedState || _startsActivated;
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
_isActivated = _startsActivated;
|
||||
if (_isActivated) OnActivate();
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,19 @@ namespace BaseGames.Puzzle
|
||||
public bool IsActive => _isActive;
|
||||
public event Action<bool> OnStateChanged;
|
||||
|
||||
private void Start() => _isActive = _startsActive;
|
||||
private void Awake()
|
||||
{
|
||||
bool savedState = !string.IsNullOrEmpty(_switchId)
|
||||
&& _worldState != null
|
||||
&& _worldState.HasFlag("switch_" + _switchId);
|
||||
_isActive = savedState || _startsActive;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_isActive && _activeClip != null) _animancer?.Play(_activeClip);
|
||||
else if (_inactiveClip != null) _animancer?.Play(_inactiveClip);
|
||||
}
|
||||
|
||||
// ── IInteractable ────────────────────────────────────────────────────
|
||||
public string InteractPrompt => _mode == SwitchTriggerMode.Hold ? "按住交互" : "交互";
|
||||
|
||||
@@ -76,10 +76,10 @@ namespace BaseGames.World.Shop
|
||||
return _availableItemsCache;
|
||||
}
|
||||
_availableItemsCache = _inventory.DefaultInventory
|
||||
.Take(_inventory.MaxDisplaySlots)
|
||||
.Where(item => item != null
|
||||
&& !_soldUniqueItems.Contains(item.ItemId)
|
||||
&& (item.MaxPurchaseCount < 0 || GetPurchaseCount(item.ItemId) < item.MaxPurchaseCount))
|
||||
.Take(_inventory.MaxDisplaySlots)
|
||||
.ToList();
|
||||
_isDirty = false;
|
||||
return _availableItemsCache;
|
||||
|
||||
@@ -3,6 +3,57 @@ using BaseGames.Equipment;
|
||||
|
||||
namespace BaseGames.World.Shop
|
||||
{
|
||||
// ─── 商品效果基类及子类 ──────────────────────────────────────────────────
|
||||
// 使用 [SerializeReference] 多态序列化:Inspector 中右键可选择具体效果类型,
|
||||
// 只显示该类型所需字段,消除原平铺字段方案中大量空字段的噪音。
|
||||
|
||||
[System.Serializable]
|
||||
public abstract class ShopItemEffect
|
||||
{
|
||||
public abstract ShopItemType Type { get; }
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class HealthRestorationEffect : ShopItemEffect
|
||||
{
|
||||
public override ShopItemType Type => ShopItemType.HealthRestoration;
|
||||
/// <summary>恢复的 HP 量。</summary>
|
||||
public int Amount;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class CharmItemEffect : ShopItemEffect
|
||||
{
|
||||
public override ShopItemType Type => ShopItemType.CharmItem;
|
||||
/// <summary>购买后解锁/获得的护符。</summary>
|
||||
public CharmSO CharmReference;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class KeyItemEffect : ShopItemEffect
|
||||
{
|
||||
public override ShopItemType Type => ShopItemType.KeyItem;
|
||||
/// <summary>授予玩家的关键道具 ID(对应 GameIds.Collectible)。</summary>
|
||||
public string KeyItemId;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class ConsumableBuffEffect : ShopItemEffect
|
||||
{
|
||||
public override ShopItemType Type => ShopItemType.ConsumableBuff;
|
||||
// 可按需在此扩展 BuffId、Duration、Magnitude 等字段
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class MapFragmentEffect : ShopItemEffect
|
||||
{
|
||||
public override ShopItemType Type => ShopItemType.MapFragment;
|
||||
/// <summary>购买后揭示的房间 ID(对应 MapManager.SetMapped)。</summary>
|
||||
public string RoomId;
|
||||
}
|
||||
|
||||
// ─── 商品 SO ──────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 商店单品 SO(架构 15_MapShopModule §2.1)。
|
||||
/// 资产路径: Assets/ScriptableObjects/Shop/Item_{ItemId}.asset
|
||||
@@ -20,15 +71,17 @@ namespace BaseGames.World.Shop
|
||||
[Header("价格")]
|
||||
public int BasePrice;
|
||||
public bool IsUnique; // 购买一次后永久从库存移除
|
||||
public int MaxPurchaseCount = -1; // -1 = 无限次
|
||||
|
||||
[Header("商品类型")]
|
||||
public ShopItemType ItemType;
|
||||
[Header("商品效果")]
|
||||
[SerializeReference]
|
||||
public ShopItemEffect Effect;
|
||||
|
||||
// 按 ItemType 填写以下字段(其余留空)
|
||||
public int HealthRestoreAmount; // HealthRestoration 类型
|
||||
public CharmSO CharmReference; // CharmItem 类型
|
||||
public string KeyItemId; // KeyItem 类型
|
||||
public int MaxPurchaseCount = -1; // -1 = 无限次
|
||||
/// <summary>
|
||||
/// 便捷属性:从 Effect 类型推导商品分类,供 ShopPanel/UI 按类型渲染图标或提示文字。
|
||||
/// Effect 为 null 时回退到 HealthRestoration(Inspector 未配置的保护值)。
|
||||
/// </summary>
|
||||
public ShopItemType ItemType => Effect?.Type ?? ShopItemType.HealthRestoration;
|
||||
}
|
||||
|
||||
public enum ShopItemType
|
||||
|
||||
Reference in New Issue
Block a user