From 2e54acbc857f25b6ffd416b5f72ad4ae1f40b261 Mon Sep 17 00:00:00 2001 From: SepComet <2428390463@qq.com> Date: Mon, 16 Mar 2026 17:52:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BA=20RepoForm=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=87=BA=E5=94=AE=E5=8A=9F=E8=83=BD=EF=BC=8CShopNode=20?= =?UTF-8?q?=E5=8F=AA=E6=89=BF=E8=BD=BD=E7=8E=A9=E5=AE=B6=E8=B4=AD=E4=B9=B0?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InventoryGeneration/ShopGoodsBuilder.cs | 13 +- .../PlayerInventoryComponent.cs | 20 + .../PlayerInventoryTradeService.cs | 400 +++++ .../PlayerInventoryTradeService.cs.meta | 11 + .../RepoSellCancelRequestedEventArgs.cs | 21 + .../RepoSellCancelRequestedEventArgs.cs.meta | 11 + .../RepoSellConfirmRequestedEventArgs.cs | 21 + .../RepoSellConfirmRequestedEventArgs.cs.meta | 11 + .../RepoSellModeToggleRequestedEventArgs.cs | 21 + ...poSellModeToggleRequestedEventArgs.cs.meta | 11 + .../UI/Game/Context/RepoFormContext.cs | 8 +- .../Scripts/UI/Game/Context/RepoFormState.cs | 8 + .../UI/Game/Context/RepoFormState.cs.meta | 11 + .../UI/Game/Context/RepoItemContext.cs | 3 + .../UI/Game/Context/SellAreaContext.cs | 10 + .../UI/Game/Context/SellAreaContext.cs.meta | 11 + .../RepoFormController.ContextBuilder.cs | 173 +- .../UI/Game/Controller/RepoFormController.cs | 111 ++ .../UI/Game/RawData/RepoFormRawData.cs | 4 + .../UI/Game/UseCase/RepoFormUseCase.cs | 145 +- .../Scripts/UI/Game/View/CombineArea.cs | 21 + .../Scripts/UI/Game/View/ParticipantArea.cs | 16 + .../GameMain/Scripts/UI/Game/View/RepoForm.cs | 41 +- .../GameMain/Scripts/UI/Game/View/RepoItem.cs | 14 +- .../GameMain/Scripts/UI/Game/View/SellArea.cs | 112 ++ .../Scripts/UI/Game/View/SellArea.cs.meta | 11 + .../UI/Shop/UseCase/ShopFormUseCase.cs | 25 +- .../Scripts/Utility/ShopPriceRuleService.cs | 122 ++ .../Utility/ShopPriceRuleService.cs.meta | 11 + Assets/GameMain/UI/UIForms/RepoForm.prefab | 1565 ++++++++++++++++- .../PlayerInventoryTradeServiceTests.cs | 298 ++++ .../PlayerInventoryTradeServiceTests.cs.meta | 11 + docs/CodeX-TODO.md | 18 +- 33 files changed, 3198 insertions(+), 91 deletions(-) create mode 100644 Assets/GameMain/Scripts/CustomComponent/PlayerInventory/PlayerInventoryTradeService.cs create mode 100644 Assets/GameMain/Scripts/CustomComponent/PlayerInventory/PlayerInventoryTradeService.cs.meta create mode 100644 Assets/GameMain/Scripts/Event/RepoForm/RepoSellCancelRequestedEventArgs.cs create mode 100644 Assets/GameMain/Scripts/Event/RepoForm/RepoSellCancelRequestedEventArgs.cs.meta create mode 100644 Assets/GameMain/Scripts/Event/RepoForm/RepoSellConfirmRequestedEventArgs.cs create mode 100644 Assets/GameMain/Scripts/Event/RepoForm/RepoSellConfirmRequestedEventArgs.cs.meta create mode 100644 Assets/GameMain/Scripts/Event/RepoForm/RepoSellModeToggleRequestedEventArgs.cs create mode 100644 Assets/GameMain/Scripts/Event/RepoForm/RepoSellModeToggleRequestedEventArgs.cs.meta create mode 100644 Assets/GameMain/Scripts/UI/Game/Context/RepoFormState.cs create mode 100644 Assets/GameMain/Scripts/UI/Game/Context/RepoFormState.cs.meta create mode 100644 Assets/GameMain/Scripts/UI/Game/Context/SellAreaContext.cs create mode 100644 Assets/GameMain/Scripts/UI/Game/Context/SellAreaContext.cs.meta create mode 100644 Assets/GameMain/Scripts/UI/Game/View/SellArea.cs create mode 100644 Assets/GameMain/Scripts/UI/Game/View/SellArea.cs.meta create mode 100644 Assets/GameMain/Scripts/Utility/ShopPriceRuleService.cs create mode 100644 Assets/GameMain/Scripts/Utility/ShopPriceRuleService.cs.meta create mode 100644 Assets/Tests/EditMode/PlayerInventoryTradeServiceTests.cs create mode 100644 Assets/Tests/EditMode/PlayerInventoryTradeServiceTests.cs.meta diff --git a/Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/ShopGoodsBuilder.cs b/Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/ShopGoodsBuilder.cs index 6a1fab3..f2b15b8 100644 --- a/Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/ShopGoodsBuilder.cs +++ b/Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/ShopGoodsBuilder.cs @@ -123,18 +123,7 @@ namespace GeometryTD.CustomComponent private int ResolveRandomPrice(RarityType rarity, Random random) { - for (int i = 0; i < _shopPriceRows.Count; i++) - { - DRShopPrice row = _shopPriceRows[i]; - if (row != null && row.Rarity == rarity) - { - int min = Mathf.Max(0, row.MinPrice); - int max = Mathf.Max(min, row.MaxPrice); - return random.Next(min, max + 1); - } - } - - return 0; + return ShopPriceRuleService.ResolveRandomBuyPrice(_shopPriceRows, rarity, random); } private static IconAreaContext BuildIconAreaContext(TowerCompItemData item) diff --git a/Assets/GameMain/Scripts/CustomComponent/PlayerInventory/PlayerInventoryComponent.cs b/Assets/GameMain/Scripts/CustomComponent/PlayerInventory/PlayerInventoryComponent.cs index bdff309..17a4766 100644 --- a/Assets/GameMain/Scripts/CustomComponent/PlayerInventory/PlayerInventoryComponent.cs +++ b/Assets/GameMain/Scripts/CustomComponent/PlayerInventory/PlayerInventoryComponent.cs @@ -14,6 +14,7 @@ namespace GeometryTD.CustomComponent private PlayerInventoryCommandModel _commandModel; private PlayerInventoryTowerRosterService _towerRosterService; private PlayerInventoryTowerAssemblyService _towerAssemblyService; + private PlayerInventoryTradeService _tradeService; public int Gold { @@ -100,6 +101,12 @@ namespace GeometryTD.CustomComponent return _commandModel.TryConsumeGold(costGold); } + public bool TryPurchaseComponent(TowerCompItemData item, int price) + { + EnsureInitialized(); + return _tradeService.TryPurchaseComponent(item, price); + } + public void AddGold(int gainGold) { EnsureInitialized(); @@ -138,6 +145,18 @@ namespace GeometryTD.CustomComponent return _towerRosterService.ReduceTowerEndurance(towerInstanceIds, enduranceLoss); } + public bool TryGetSaleCandidate(long itemId, out PlayerInventorySaleCandidate candidate) + { + EnsureInitialized(); + return _tradeService.TryGetSaleCandidate(itemId, out candidate); + } + + public bool TrySellItems(IReadOnlyCollection itemIds, out PlayerInventorySaleResult result) + { + EnsureInitialized(); + return _tradeService.TrySellItems(itemIds, out result); + } + private void EnsureInitialized() { if (_queryModel.IsInitialized) @@ -155,6 +174,7 @@ namespace GeometryTD.CustomComponent _commandModel ??= new PlayerInventoryCommandModel(_state); _towerRosterService ??= new PlayerInventoryTowerRosterService(_queryModel, MaxParticipantTowerCount); _towerAssemblyService ??= new PlayerInventoryTowerAssemblyService(_queryModel, _commandModel); + _tradeService ??= new PlayerInventoryTradeService(_queryModel, _commandModel); } } } diff --git a/Assets/GameMain/Scripts/CustomComponent/PlayerInventory/PlayerInventoryTradeService.cs b/Assets/GameMain/Scripts/CustomComponent/PlayerInventory/PlayerInventoryTradeService.cs new file mode 100644 index 0000000..65c569d --- /dev/null +++ b/Assets/GameMain/Scripts/CustomComponent/PlayerInventory/PlayerInventoryTradeService.cs @@ -0,0 +1,400 @@ +using System.Collections.Generic; +using GameFramework.DataTable; +using GeometryTD.CustomUtility; +using GeometryTD.DataTable; +using GeometryTD.Definition; +using UnityEngine; + +namespace GeometryTD.CustomComponent +{ + public enum PlayerInventorySaleFailureReason : byte + { + None = 0, + InvalidSelection = 1, + ItemNotFound = 2, + AssembledComponent = 3, + ParticipantTower = 4, + MissingTowerComponent = 5 + } + + public sealed class PlayerInventorySaleCandidate + { + public long ItemId; + public bool IsSellable; + public bool IsTower; + public int Price; + public PlayerInventorySaleFailureReason FailureReason; + } + + public sealed class PlayerInventorySaleResult + { + public int GainedGold; + public int SoldComponentCount; + public int SoldTowerCount; + public PlayerInventorySaleFailureReason FailureReason; + + public bool IsSuccess => FailureReason == PlayerInventorySaleFailureReason.None; + public int SoldItemCount => SoldComponentCount + SoldTowerCount; + } + + public sealed class PlayerInventoryTradeService + { + private readonly PlayerInventoryQueryModel _queryModel; + private readonly PlayerInventoryCommandModel _commandModel; + private IDataTable _shopPriceTable; + + public PlayerInventoryTradeService( + PlayerInventoryQueryModel queryModel, + PlayerInventoryCommandModel commandModel, + IDataTable shopPriceTable = null) + { + _queryModel = queryModel; + _commandModel = commandModel; + _shopPriceTable = shopPriceTable; + } + + public bool TryPurchaseComponent(TowerCompItemData item, int price) + { + if (item == null) + { + return false; + } + + if (!_commandModel.TryConsumeGold(price)) + { + return false; + } + + BackpackInventoryData inventoryDelta = WrapSingleItem(item); + PlayerInventoryMergeSummary summary = _commandModel.MergeInventory(inventoryDelta); + return summary.HasAnyGain; + } + + public bool TryGetSaleCandidate(long itemId, out PlayerInventorySaleCandidate candidate) + { + candidate = BuildSaleCandidate(itemId); + return candidate != null; + } + + public bool TrySellItems(IReadOnlyCollection itemIds, out PlayerInventorySaleResult result) + { + result = new PlayerInventorySaleResult(); + if (itemIds == null || itemIds.Count <= 0) + { + result.FailureReason = PlayerInventorySaleFailureReason.InvalidSelection; + return false; + } + + HashSet uniqueIds = new HashSet(); + List candidates = new List(itemIds.Count); + foreach (long itemId in itemIds) + { + if (itemId <= 0 || !uniqueIds.Add(itemId)) + { + continue; + } + + PlayerInventorySaleCandidate candidate = BuildSaleCandidate(itemId); + if (candidate == null || !candidate.IsSellable) + { + result.FailureReason = candidate?.FailureReason ?? PlayerInventorySaleFailureReason.ItemNotFound; + return false; + } + + candidates.Add(candidate); + } + + if (candidates.Count <= 0) + { + result.FailureReason = PlayerInventorySaleFailureReason.InvalidSelection; + return false; + } + + BackpackInventoryData inventory = _queryModel.Inventory; + for (int i = 0; i < candidates.Count; i++) + { + PlayerInventorySaleCandidate candidate = candidates[i]; + if (candidate.IsTower) + { + if (!TryRemoveTower(inventory, candidate.ItemId)) + { + result.FailureReason = PlayerInventorySaleFailureReason.MissingTowerComponent; + return false; + } + + result.SoldTowerCount++; + } + else + { + if (!TryRemoveComponent(inventory, candidate.ItemId)) + { + result.FailureReason = PlayerInventorySaleFailureReason.ItemNotFound; + return false; + } + + result.SoldComponentCount++; + } + + result.GainedGold += Mathf.Max(0, candidate.Price); + } + + _commandModel.AddGold(result.GainedGold); + result.FailureReason = PlayerInventorySaleFailureReason.None; + return true; + } + + private PlayerInventorySaleCandidate BuildSaleCandidate(long itemId) + { + if (itemId <= 0) + { + return new PlayerInventorySaleCandidate + { + ItemId = itemId, + IsSellable = false, + FailureReason = PlayerInventorySaleFailureReason.InvalidSelection + }; + } + + BackpackInventoryData inventory = _queryModel.Inventory; + if (inventory == null) + { + return new PlayerInventorySaleCandidate + { + ItemId = itemId, + IsSellable = false, + FailureReason = PlayerInventorySaleFailureReason.ItemNotFound + }; + } + + if (_queryModel.TryGetTowerById(itemId, out TowerItemData tower) && tower != null) + { + if (inventory.ParticipantTowerInstanceIds != null && inventory.ParticipantTowerInstanceIds.Contains(itemId)) + { + return new PlayerInventorySaleCandidate + { + ItemId = itemId, + IsSellable = false, + IsTower = true, + FailureReason = PlayerInventorySaleFailureReason.ParticipantTower + }; + } + + if (!ShopPriceRuleService.TryResolveTowerSalePrice(tower, inventory, out int towerPrice, EnsureShopPriceTable())) + { + return new PlayerInventorySaleCandidate + { + ItemId = itemId, + IsSellable = false, + IsTower = true, + FailureReason = PlayerInventorySaleFailureReason.MissingTowerComponent + }; + } + + return new PlayerInventorySaleCandidate + { + ItemId = itemId, + IsSellable = true, + IsTower = true, + Price = towerPrice, + FailureReason = PlayerInventorySaleFailureReason.None + }; + } + + if (TryGetComponentById(inventory.MuzzleComponents, itemId, out MuzzleCompItemData muzzleComp)) + { + return BuildComponentCandidate(muzzleComp); + } + + if (TryGetComponentById(inventory.BearingComponents, itemId, out BearingCompItemData bearingComp)) + { + return BuildComponentCandidate(bearingComp); + } + + if (TryGetComponentById(inventory.BaseComponents, itemId, out BaseCompItemData baseComp)) + { + return BuildComponentCandidate(baseComp); + } + + return new PlayerInventorySaleCandidate + { + ItemId = itemId, + IsSellable = false, + FailureReason = PlayerInventorySaleFailureReason.ItemNotFound + }; + } + + private PlayerInventorySaleCandidate BuildComponentCandidate(TowerCompItemData component) + { + if (component == null) + { + return new PlayerInventorySaleCandidate + { + IsSellable = false, + FailureReason = PlayerInventorySaleFailureReason.ItemNotFound + }; + } + + if (component.IsAssembledIntoTower) + { + return new PlayerInventorySaleCandidate + { + ItemId = component.InstanceId, + IsSellable = false, + FailureReason = PlayerInventorySaleFailureReason.AssembledComponent + }; + } + + return new PlayerInventorySaleCandidate + { + ItemId = component.InstanceId, + IsSellable = true, + IsTower = false, + Price = ShopPriceRuleService.ResolveComponentSalePrice(component, EnsureShopPriceTable()), + FailureReason = PlayerInventorySaleFailureReason.None + }; + } + + private bool TryRemoveTower(BackpackInventoryData inventory, long towerId) + { + if (inventory?.Towers == null || towerId <= 0) + { + return false; + } + + TowerItemData targetTower = null; + for (int i = 0; i < inventory.Towers.Count; i++) + { + TowerItemData tower = inventory.Towers[i]; + if (tower != null && tower.InstanceId == towerId) + { + targetTower = tower; + break; + } + } + + if (targetTower == null) + { + return false; + } + + if (!ContainsInstanceId(inventory.MuzzleComponents, targetTower.MuzzleComponentInstanceId) || + !ContainsInstanceId(inventory.BearingComponents, targetTower.BearingComponentInstanceId) || + !ContainsInstanceId(inventory.BaseComponents, targetTower.BaseComponentInstanceId)) + { + return false; + } + + bool removedMuzzle = RemoveByInstanceId(inventory.MuzzleComponents, targetTower.MuzzleComponentInstanceId); + bool removedBearing = RemoveByInstanceId(inventory.BearingComponents, targetTower.BearingComponentInstanceId); + bool removedBase = RemoveByInstanceId(inventory.BaseComponents, targetTower.BaseComponentInstanceId); + if (!removedMuzzle || !removedBearing || !removedBase) + { + return false; + } + + inventory.Towers.Remove(targetTower); + inventory.ParticipantTowerInstanceIds?.Remove(towerId); + return true; + } + + private static bool TryRemoveComponent(BackpackInventoryData inventory, long itemId) + { + return RemoveByInstanceId(inventory?.MuzzleComponents, itemId) || + RemoveByInstanceId(inventory?.BearingComponents, itemId) || + RemoveByInstanceId(inventory?.BaseComponents, itemId); + } + + private static bool RemoveByInstanceId(List items, long instanceId) + where TItem : class + { + if (items == null || instanceId <= 0) + { + return false; + } + + for (int i = 0; i < items.Count; i++) + { + switch (items[i]) + { + case TowerCompItemData component when component.InstanceId == instanceId: + items.RemoveAt(i); + return true; + case TowerItemData tower when tower.InstanceId == instanceId: + items.RemoveAt(i); + return true; + } + } + + return false; + } + + private static bool ContainsInstanceId(IReadOnlyList items, long instanceId) + where TItem : class + { + if (items == null || instanceId <= 0) + { + return false; + } + + for (int i = 0; i < items.Count; i++) + { + switch (items[i]) + { + case TowerCompItemData component when component.InstanceId == instanceId: + return true; + case TowerItemData tower when tower.InstanceId == instanceId: + return true; + } + } + + return false; + } + + private static bool TryGetComponentById(IReadOnlyList items, long instanceId, out TComp result) + where TComp : TowerCompItemData + { + result = null; + if (items == null || instanceId <= 0) + { + return false; + } + + for (int i = 0; i < items.Count; i++) + { + TComp item = items[i]; + if (item != null && item.InstanceId == instanceId) + { + result = item; + return true; + } + } + + return false; + } + + private static BackpackInventoryData WrapSingleItem(TowerCompItemData item) + { + BackpackInventoryData inventory = new BackpackInventoryData(); + switch (item) + { + case MuzzleCompItemData muzzleComp: + inventory.MuzzleComponents.Add(InventoryCloneUtility.CloneMuzzleComp(muzzleComp)); + break; + case BearingCompItemData bearingComp: + inventory.BearingComponents.Add(InventoryCloneUtility.CloneBearingComp(bearingComp)); + break; + case BaseCompItemData baseComp: + inventory.BaseComponents.Add(InventoryCloneUtility.CloneBaseComp(baseComp)); + break; + } + + return inventory; + } + + private IDataTable EnsureShopPriceTable() + { + _shopPriceTable ??= GameEntry.DataTable.GetDataTable(); + return _shopPriceTable; + } + } +} diff --git a/Assets/GameMain/Scripts/CustomComponent/PlayerInventory/PlayerInventoryTradeService.cs.meta b/Assets/GameMain/Scripts/CustomComponent/PlayerInventory/PlayerInventoryTradeService.cs.meta new file mode 100644 index 0000000..20614d4 --- /dev/null +++ b/Assets/GameMain/Scripts/CustomComponent/PlayerInventory/PlayerInventoryTradeService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 298cf9214556453cb5e7ec7c0605c350 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Event/RepoForm/RepoSellCancelRequestedEventArgs.cs b/Assets/GameMain/Scripts/Event/RepoForm/RepoSellCancelRequestedEventArgs.cs new file mode 100644 index 0000000..5ad03b0 --- /dev/null +++ b/Assets/GameMain/Scripts/Event/RepoForm/RepoSellCancelRequestedEventArgs.cs @@ -0,0 +1,21 @@ +using GameFramework; +using GameFramework.Event; + +namespace GeometryTD.CustomEvent +{ + public sealed class RepoSellCancelRequestedEventArgs : GameEventArgs + { + public static int EventId => typeof(RepoSellCancelRequestedEventArgs).GetHashCode(); + + public override int Id => EventId; + + public static RepoSellCancelRequestedEventArgs Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Event/RepoForm/RepoSellCancelRequestedEventArgs.cs.meta b/Assets/GameMain/Scripts/Event/RepoForm/RepoSellCancelRequestedEventArgs.cs.meta new file mode 100644 index 0000000..85ebb37 --- /dev/null +++ b/Assets/GameMain/Scripts/Event/RepoForm/RepoSellCancelRequestedEventArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2cc34a22ea6347619b065c5874e0b715 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Event/RepoForm/RepoSellConfirmRequestedEventArgs.cs b/Assets/GameMain/Scripts/Event/RepoForm/RepoSellConfirmRequestedEventArgs.cs new file mode 100644 index 0000000..ce0557b --- /dev/null +++ b/Assets/GameMain/Scripts/Event/RepoForm/RepoSellConfirmRequestedEventArgs.cs @@ -0,0 +1,21 @@ +using GameFramework; +using GameFramework.Event; + +namespace GeometryTD.CustomEvent +{ + public sealed class RepoSellConfirmRequestedEventArgs : GameEventArgs + { + public static int EventId => typeof(RepoSellConfirmRequestedEventArgs).GetHashCode(); + + public override int Id => EventId; + + public static RepoSellConfirmRequestedEventArgs Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Event/RepoForm/RepoSellConfirmRequestedEventArgs.cs.meta b/Assets/GameMain/Scripts/Event/RepoForm/RepoSellConfirmRequestedEventArgs.cs.meta new file mode 100644 index 0000000..80651b7 --- /dev/null +++ b/Assets/GameMain/Scripts/Event/RepoForm/RepoSellConfirmRequestedEventArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 598b87b9cb8e4783bc73db5e255d31f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Event/RepoForm/RepoSellModeToggleRequestedEventArgs.cs b/Assets/GameMain/Scripts/Event/RepoForm/RepoSellModeToggleRequestedEventArgs.cs new file mode 100644 index 0000000..e96c3e0 --- /dev/null +++ b/Assets/GameMain/Scripts/Event/RepoForm/RepoSellModeToggleRequestedEventArgs.cs @@ -0,0 +1,21 @@ +using GameFramework; +using GameFramework.Event; + +namespace GeometryTD.CustomEvent +{ + public sealed class RepoSellModeToggleRequestedEventArgs : GameEventArgs + { + public static int EventId => typeof(RepoSellModeToggleRequestedEventArgs).GetHashCode(); + + public override int Id => EventId; + + public static RepoSellModeToggleRequestedEventArgs Create() + { + return ReferencePool.Acquire(); + } + + public override void Clear() + { + } + } +} diff --git a/Assets/GameMain/Scripts/Event/RepoForm/RepoSellModeToggleRequestedEventArgs.cs.meta b/Assets/GameMain/Scripts/Event/RepoForm/RepoSellModeToggleRequestedEventArgs.cs.meta new file mode 100644 index 0000000..53380f8 --- /dev/null +++ b/Assets/GameMain/Scripts/Event/RepoForm/RepoSellModeToggleRequestedEventArgs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 44692273ce6a417a875dea9675663d82 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/UI/Game/Context/RepoFormContext.cs b/Assets/GameMain/Scripts/UI/Game/Context/RepoFormContext.cs index cf3fffb..0931b88 100644 --- a/Assets/GameMain/Scripts/UI/Game/Context/RepoFormContext.cs +++ b/Assets/GameMain/Scripts/UI/Game/Context/RepoFormContext.cs @@ -1,11 +1,15 @@ -using GeometryTD.UI; - namespace GeometryTD.UI { public class RepoFormContext : UIContext { public string GoldText; + public RepoFormState State; + public bool ShowSellModeButton; + public string SellModeButtonText; + public bool ShowCombineArea; + public bool ShowSellArea; public CombineAreaContext CombineAreaContext; + public SellAreaContext SellAreaContext; public CompAreaContext CompAreaContext; public ParticipantAreaContext ParticipantAreaContext; } diff --git a/Assets/GameMain/Scripts/UI/Game/Context/RepoFormState.cs b/Assets/GameMain/Scripts/UI/Game/Context/RepoFormState.cs new file mode 100644 index 0000000..57a6f4c --- /dev/null +++ b/Assets/GameMain/Scripts/UI/Game/Context/RepoFormState.cs @@ -0,0 +1,8 @@ +namespace GeometryTD.UI +{ + public enum RepoFormState + { + Assemble = 0, + Sell = 1 + } +} diff --git a/Assets/GameMain/Scripts/UI/Game/Context/RepoFormState.cs.meta b/Assets/GameMain/Scripts/UI/Game/Context/RepoFormState.cs.meta new file mode 100644 index 0000000..7e39b40 --- /dev/null +++ b/Assets/GameMain/Scripts/UI/Game/Context/RepoFormState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9110fc764f3044bda40df4a8c02f320b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/UI/Game/Context/RepoItemContext.cs b/Assets/GameMain/Scripts/UI/Game/Context/RepoItemContext.cs index 2305a21..31caa47 100644 --- a/Assets/GameMain/Scripts/UI/Game/Context/RepoItemContext.cs +++ b/Assets/GameMain/Scripts/UI/Game/Context/RepoItemContext.cs @@ -11,6 +11,9 @@ namespace GeometryTD.UI public float EnduranceRate01; public RepoItemClickActionType ClickActionType; public TowerCompSlotType ComponentSlotType; + public bool IsSellMode; + public bool IsSellable = true; + public bool IsSellSelected; public IconAreaContext IconAreaContext; } } diff --git a/Assets/GameMain/Scripts/UI/Game/Context/SellAreaContext.cs b/Assets/GameMain/Scripts/UI/Game/Context/SellAreaContext.cs new file mode 100644 index 0000000..2b7c00c --- /dev/null +++ b/Assets/GameMain/Scripts/UI/Game/Context/SellAreaContext.cs @@ -0,0 +1,10 @@ +namespace GeometryTD.UI +{ + public class SellAreaContext + { + public string TotalPriceText; + public bool CanConfirmSell; + public RepoItemContext[] ComponentItems; + public TowerRepoItemContext[] TowerItems; + } +} diff --git a/Assets/GameMain/Scripts/UI/Game/Context/SellAreaContext.cs.meta b/Assets/GameMain/Scripts/UI/Game/Context/SellAreaContext.cs.meta new file mode 100644 index 0000000..669e349 --- /dev/null +++ b/Assets/GameMain/Scripts/UI/Game/Context/SellAreaContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f153d1a509df4bb69c6d231df5b60db9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/UI/Game/ContextBuilder/RepoFormController.ContextBuilder.cs b/Assets/GameMain/Scripts/UI/Game/ContextBuilder/RepoFormController.ContextBuilder.cs index cc63c0d..c8e8c08 100644 --- a/Assets/GameMain/Scripts/UI/Game/ContextBuilder/RepoFormController.ContextBuilder.cs +++ b/Assets/GameMain/Scripts/UI/Game/ContextBuilder/RepoFormController.ContextBuilder.cs @@ -23,21 +23,44 @@ namespace GeometryTD.UI Dictionary bearingMap = BuildComponentMap(rawData.Inventory.BearingComponents); Dictionary baseMap = BuildComponentMap(rawData.Inventory.BaseComponents); Dictionary towerMap = BuildTowerMap(rawData.Inventory.Towers); + HashSet selectedSellItemIds = BuildSelectedSellItemSet(rawData.SelectedSellItemIds); + bool isSellState = rawData.State == RepoFormState.Sell; + List componentItems = new List(); List towerItems = new List(); + List selectedSellComponentItems = new List(); + List selectedSellTowerItems = new List(); if (rawData.Inventory.Towers != null) { - foreach (var tower in rawData.Inventory.Towers) + foreach (TowerItemData tower in rawData.Inventory.Towers) { if (tower == null) { continue; } - TowerRepoItemContext towerContext = BuildTowerRepoItemContext(tower, muzzleMap, bearingMap, baseMap, - RepoItemClickActionType.OpenDetail, true); + bool isParticipantTower = rawData.Inventory.ParticipantTowerInstanceIds != null && + rawData.Inventory.ParticipantTowerInstanceIds.Contains(tower.InstanceId); + bool isSellSelected = isSellState && selectedSellItemIds.Contains(tower.InstanceId); + + TowerRepoItemContext towerContext = BuildTowerRepoItemContext( + tower, + muzzleMap, + bearingMap, + baseMap, + RepoItemClickActionType.OpenDetail, + !isSellState, + isSellState, + !isParticipantTower, + isSellSelected, + false); AddTowerItemContext(towerItems, towerContext); + if (isSellSelected) + { + selectedSellTowerItems.Add(towerContext); + } + AddItemDescSeed( tower.InstanceId, tower.Name, @@ -50,23 +73,24 @@ namespace GeometryTD.UI if (rawData.Inventory.MuzzleComponents != null) { - foreach (var item in rawData.Inventory.MuzzleComponents) + foreach (MuzzleCompItemData item in rawData.Inventory.MuzzleComponents) { if (item == null || item.IsAssembledIntoTower) { continue; } - RepoItemContext componentContext = new RepoItemContext - { - InstanceId = item.InstanceId, - CanDrag = true, - EnduranceRate01 = ItemDescUtility.ResolveComponentEnduranceRate(item), - ClickActionType = RepoItemClickActionType.OpenDetail, - ComponentSlotType = TowerCompSlotType.Muzzle, - IconAreaContext = BuildIconAreaContext(item) - }; + RepoItemContext componentContext = BuildSellableComponentContext( + item, + TowerCompSlotType.Muzzle, + isSellState, + selectedSellItemIds.Contains(item.InstanceId)); AddComponentItemContext(componentItems, componentContext); + if (componentContext.IsSellSelected) + { + selectedSellComponentItems.Add(componentContext); + } + AddItemDescSeed( item.InstanceId, item.Name, @@ -86,16 +110,17 @@ namespace GeometryTD.UI continue; } - RepoItemContext componentContext = new RepoItemContext - { - InstanceId = item.InstanceId, - CanDrag = true, - EnduranceRate01 = ItemDescUtility.ResolveComponentEnduranceRate(item), - ClickActionType = RepoItemClickActionType.OpenDetail, - ComponentSlotType = TowerCompSlotType.Bearing, - IconAreaContext = BuildIconAreaContext(item) - }; + RepoItemContext componentContext = BuildSellableComponentContext( + item, + TowerCompSlotType.Bearing, + isSellState, + selectedSellItemIds.Contains(item.InstanceId)); AddComponentItemContext(componentItems, componentContext); + if (componentContext.IsSellSelected) + { + selectedSellComponentItems.Add(componentContext); + } + AddItemDescSeed( item.InstanceId, item.Name, @@ -115,16 +140,17 @@ namespace GeometryTD.UI continue; } - RepoItemContext componentContext = new RepoItemContext - { - InstanceId = item.InstanceId, - CanDrag = true, - EnduranceRate01 = ItemDescUtility.ResolveComponentEnduranceRate(item), - ClickActionType = RepoItemClickActionType.OpenDetail, - ComponentSlotType = TowerCompSlotType.Base, - IconAreaContext = BuildIconAreaContext(item) - }; + RepoItemContext componentContext = BuildSellableComponentContext( + item, + TowerCompSlotType.Base, + isSellState, + selectedSellItemIds.Contains(item.InstanceId)); AddComponentItemContext(componentItems, componentContext); + if (componentContext.IsSellSelected) + { + selectedSellComponentItems.Add(componentContext); + } + AddItemDescSeed( item.InstanceId, item.Name, @@ -141,7 +167,19 @@ namespace GeometryTD.UI return new RepoFormContext { GoldText = $"金币: {rawData.Inventory.Gold}", + State = rawData.State, + ShowSellModeButton = rawData.State == RepoFormState.Assemble, + SellModeButtonText = "出售模式", + ShowCombineArea = rawData.State == RepoFormState.Assemble, + ShowSellArea = rawData.State == RepoFormState.Sell, CombineAreaContext = new CombineAreaContext(), + SellAreaContext = new SellAreaContext + { + TotalPriceText = $"总价值: {rawData.SelectedSellTotalPrice}", + CanConfirmSell = rawData.State == RepoFormState.Sell && rawData.SelectedSellItemCount > 0, + ComponentItems = selectedSellComponentItems.ToArray(), + TowerItems = selectedSellTowerItems.ToArray() + }, CompAreaContext = new CompAreaContext { ComponentItems = componentItems.ToArray(), @@ -151,6 +189,26 @@ namespace GeometryTD.UI }; } + private static RepoItemContext BuildSellableComponentContext( + TowerCompItemData item, + TowerCompSlotType slotType, + bool isSellState, + bool isSellSelected) + { + return new RepoItemContext + { + InstanceId = item.InstanceId, + CanDrag = !isSellState, + EnduranceRate01 = ItemDescUtility.ResolveComponentEnduranceRate(item), + ClickActionType = RepoItemClickActionType.OpenDetail, + ComponentSlotType = slotType, + IsSellMode = isSellState, + IsSellable = true, + IsSellSelected = isSellState && isSellSelected, + IconAreaContext = BuildIconAreaContext(item) + }; + } + private void AddComponentItemContext(List items, RepoItemContext itemContext) { if (itemContext == null) @@ -218,7 +276,7 @@ namespace GeometryTD.UI return map; } - foreach (var item in items) + foreach (TComp item in items) { if (item == null || item.InstanceId <= 0) { @@ -231,6 +289,26 @@ namespace GeometryTD.UI return map; } + private static HashSet BuildSelectedSellItemSet(IReadOnlyList selectedSellItemIds) + { + HashSet selectedIds = new HashSet(); + if (selectedSellItemIds == null) + { + return selectedIds; + } + + for (int i = 0; i < selectedSellItemIds.Count; i++) + { + long itemId = selectedSellItemIds[i]; + if (itemId > 0) + { + selectedIds.Add(itemId); + } + } + + return selectedIds; + } + private static Dictionary BuildTowerMap(IReadOnlyList towers) { Dictionary map = new Dictionary(); @@ -239,7 +317,7 @@ namespace GeometryTD.UI return map; } - foreach (var tower in towers) + foreach (TowerItemData tower in towers) { if (tower == null || tower.InstanceId <= 0) { @@ -263,7 +341,7 @@ namespace GeometryTD.UI List participantItems = new List(); if (inventory?.ParticipantTowerInstanceIds != null && towerMap != null) { - foreach (var towerId in inventory.ParticipantTowerInstanceIds) + foreach (long towerId in inventory.ParticipantTowerInstanceIds) { if (towerId <= 0) { @@ -280,8 +358,17 @@ namespace GeometryTD.UI continue; } - TowerRepoItemContext towerContext = BuildTowerRepoItemContext(tower, muzzleMap, bearingMap, baseMap, - RepoItemClickActionType.RemoveParticipant, false); + TowerRepoItemContext towerContext = BuildTowerRepoItemContext( + tower, + muzzleMap, + bearingMap, + baseMap, + RepoItemClickActionType.RemoveParticipant, + false, + false, + false, + false, + false); if (towerContext != null) { participantItems.Add(towerContext); @@ -304,6 +391,11 @@ namespace GeometryTD.UI return; } + if (Context != null && Context.State == RepoFormState.Sell) + { + return; + } + foreach (long towerId in _compAreaTowerIds) { Form.SetRepoItemSelected(towerId, _participantTowerIds.Contains(towerId)); @@ -334,7 +426,11 @@ namespace GeometryTD.UI IReadOnlyDictionary bearingMap, IReadOnlyDictionary baseMap, RepoItemClickActionType clickActionType, - bool canDrag) + bool canDrag, + bool isSellMode, + bool isSellable, + bool isSellSelected, + bool highlightSelected) { if (tower == null) { @@ -348,6 +444,9 @@ namespace GeometryTD.UI EnduranceRate01 = ItemDescUtility.ResolveTowerEnduranceRate(tower, muzzleMap, bearingMap, baseMap), ClickActionType = clickActionType, ComponentSlotType = TowerCompSlotType.None, + IsSellMode = isSellMode, + IsSellable = isSellable, + IsSellSelected = isSellSelected || highlightSelected, IconAreaContext = new TowerIconAreaContext { Rarity = tower.Rarity, diff --git a/Assets/GameMain/Scripts/UI/Game/Controller/RepoFormController.cs b/Assets/GameMain/Scripts/UI/Game/Controller/RepoFormController.cs index 7870dfc..45e319b 100644 --- a/Assets/GameMain/Scripts/UI/Game/Controller/RepoFormController.cs +++ b/Assets/GameMain/Scripts/UI/Game/Controller/RepoFormController.cs @@ -3,6 +3,7 @@ using GeometryTD.CustomEvent; using GeometryTD.CustomUtility; using GeometryTD.Definition; using GameFramework.Event; +using GeometryTD.CustomComponent; using UnityEngine; using UnityGameFramework.Runtime; @@ -43,6 +44,9 @@ namespace GeometryTD.UI GameEntry.Event.Subscribe(RepoCombineRequestedEventArgs.EventId, OnRepoCombineRequested); GameEntry.Event.Subscribe(RepoParticipantAssignRequestedEventArgs.EventId, OnRepoParticipantAssignRequested); GameEntry.Event.Subscribe(RepoFormReturnEventArgs.EventId, OnRepoFormReturn); + GameEntry.Event.Subscribe(RepoSellModeToggleRequestedEventArgs.EventId, OnRepoSellModeToggleRequested); + GameEntry.Event.Subscribe(RepoSellCancelRequestedEventArgs.EventId, OnRepoSellCancelRequested); + GameEntry.Event.Subscribe(RepoSellConfirmRequestedEventArgs.EventId, OnRepoSellConfirmRequested); } protected override void UnsubscribeCustomEvents() @@ -53,6 +57,9 @@ namespace GeometryTD.UI GameEntry.Event.Unsubscribe(RepoCombineRequestedEventArgs.EventId, OnRepoCombineRequested); GameEntry.Event.Unsubscribe(RepoParticipantAssignRequestedEventArgs.EventId, OnRepoParticipantAssignRequested); GameEntry.Event.Unsubscribe(RepoFormReturnEventArgs.EventId, OnRepoFormReturn); + GameEntry.Event.Unsubscribe(RepoSellModeToggleRequestedEventArgs.EventId, OnRepoSellModeToggleRequested); + GameEntry.Event.Unsubscribe(RepoSellCancelRequestedEventArgs.EventId, OnRepoSellCancelRequested); + GameEntry.Event.Unsubscribe(RepoSellConfirmRequestedEventArgs.EventId, OnRepoSellConfirmRequested); } public override int? OpenUI(object userData = null) @@ -125,6 +132,24 @@ namespace GeometryTD.UI return; } + if (_useCase != null && _useCase.State == RepoFormState.Sell) + { + if (_useCase.TryToggleSellSelection( + args.ItemId, + out RepoFormRawData sellModeRawData, + out PlayerInventorySaleFailureReason failureReason)) + { + SetContext(BuildContext(sellModeRawData)); + RefreshCurrentUI(); + } + else if (failureReason == PlayerInventorySaleFailureReason.ParticipantTower) + { + OpenSellBlockedDialog("参战防御塔不能直接出售,请先移出参战区。"); + } + + return; + } + if (clickActionType == RepoItemClickActionType.RemoveParticipant) { if (_useCase == null || Form == null) @@ -165,6 +190,11 @@ namespace GeometryTD.UI return; } + if (Context != null && Context.State == RepoFormState.Sell) + { + return; + } + if (!(e is RepoItemDragEndedEventArgs args)) { return; @@ -188,6 +218,11 @@ namespace GeometryTD.UI return; } + if (Context != null && Context.State == RepoFormState.Sell) + { + return; + } + if (!(e is CombineSlotClickedEventArgs args)) { return; @@ -257,6 +292,11 @@ namespace GeometryTD.UI return; } + if (Context != null && Context.State == RepoFormState.Sell) + { + return; + } + if (!(e is RepoParticipantAssignRequestedEventArgs args)) { return; @@ -288,6 +328,65 @@ namespace GeometryTD.UI RefreshParticipantAreaOnly(); } + private void OnRepoSellModeToggleRequested(object sender, GameEventArgs e) + { + if (!IsEventFromCurrentForm(sender) || !(e is RepoSellModeToggleRequestedEventArgs) || _useCase == null) + { + return; + } + + if (_useCase.State != RepoFormState.Assemble) + { + return; + } + + RepoFormRawData rawData = _useCase.EnterSellState(); + SetContext(BuildContext(rawData)); + RefreshCurrentUI(); + } + + private void OnRepoSellCancelRequested(object sender, GameEventArgs e) + { + if (!IsEventFromCurrentForm(sender) || !(e is RepoSellCancelRequestedEventArgs) || _useCase == null) + { + return; + } + + if (_useCase.State != RepoFormState.Sell) + { + return; + } + + SetContext(BuildContext(_useCase.ExitSellState())); + RefreshCurrentUI(); + } + + private void OnRepoSellConfirmRequested(object sender, GameEventArgs e) + { + if (!IsEventFromCurrentForm(sender) || !(e is RepoSellConfirmRequestedEventArgs) || _useCase == null) + { + return; + } + + if (!_useCase.TryConfirmSellSelection(out RepoFormRawData rawData, out PlayerInventorySaleResult result)) + { + if (result != null && result.FailureReason == PlayerInventorySaleFailureReason.ParticipantTower) + { + OpenSellBlockedDialog("参战防御塔不能直接出售,请先移出参战区。"); + } + + return; + } + + SetContext(BuildContext(rawData)); + RefreshCurrentUI(); + + if (GameEntry.UI.GetUIForm(UIFormType.ShopForm) != null) + { + GameEntry.UIRouter.OpenUI(UIFormType.ShopForm); + } + } + private bool IsEventFromCurrentForm(object sender) { if (Form == null) @@ -309,6 +408,18 @@ namespace GeometryTD.UI return false; } + private static void OpenSellBlockedDialog(string message) + { + GameEntry.UIRouter.OpenUI(UIFormType.DialogForm, new DialogFormRawData + { + Mode = 1, + Title = "无法出售", + Message = message, + PauseGame = false, + ConfirmText = "知道了" + }); + } + #endregion } } diff --git a/Assets/GameMain/Scripts/UI/Game/RawData/RepoFormRawData.cs b/Assets/GameMain/Scripts/UI/Game/RawData/RepoFormRawData.cs index 929c371..fa01232 100644 --- a/Assets/GameMain/Scripts/UI/Game/RawData/RepoFormRawData.cs +++ b/Assets/GameMain/Scripts/UI/Game/RawData/RepoFormRawData.cs @@ -5,5 +5,9 @@ namespace GeometryTD.UI public class RepoFormRawData { public BackpackInventoryData Inventory; + public RepoFormState State; + public long[] SelectedSellItemIds; + public int SelectedSellItemCount; + public int SelectedSellTotalPrice; } } diff --git a/Assets/GameMain/Scripts/UI/Game/UseCase/RepoFormUseCase.cs b/Assets/GameMain/Scripts/UI/Game/UseCase/RepoFormUseCase.cs index bfb14d2..7142f7f 100644 --- a/Assets/GameMain/Scripts/UI/Game/UseCase/RepoFormUseCase.cs +++ b/Assets/GameMain/Scripts/UI/Game/UseCase/RepoFormUseCase.cs @@ -1,4 +1,6 @@ -using GeometryTD.CustomUtility; +using System.Collections.Generic; +using GeometryTD.CustomComponent; +using GeometryTD.CustomUtility; using GeometryTD.Definition; namespace GeometryTD.UI @@ -7,6 +9,8 @@ namespace GeometryTD.UI { private const int MaxParticipantCount = 4; private BackpackInventoryData _fallbackInventory; + private readonly HashSet _selectedSellItemIds = new HashSet(); + private RepoFormState _state = RepoFormState.Assemble; public RepoFormRawData CreateInitialModel() { @@ -15,12 +19,23 @@ namespace GeometryTD.UI : GetOrCreateFallbackInventory(); return new RepoFormRawData { - Inventory = sample + Inventory = sample, + State = _state, + SelectedSellItemIds = BuildSelectedSellItemArray(), + SelectedSellItemCount = _selectedSellItemIds.Count, + SelectedSellTotalPrice = ResolveSelectedSellTotalPrice() }; } + public RepoFormState State => _state; + public bool TryAssembleTower(long muzzleItemId, long bearingItemId, long baseItemId) { + if (_state == RepoFormState.Sell) + { + return false; + } + if (GameEntry.PlayerInventory == null) { return false; @@ -35,6 +50,15 @@ namespace GeometryTD.UI public ParticipantTowerAssignResult TryAddParticipantTower(long towerItemId) { + if (_state == RepoFormState.Sell) + { + return new ParticipantTowerAssignResult + { + TowerInstanceId = towerItemId, + FailureReason = ParticipantTowerAssignFailureReason.ParticipantAreaFull + }; + } + if (GameEntry.PlayerInventory == null) { BackpackInventoryData fallbackInventory = GetOrCreateFallbackInventory(); @@ -44,11 +68,16 @@ namespace GeometryTD.UI MaxParticipantCount); } - return GameEntry.PlayerInventory.TryAddParticipantTower(towerItemId, 4); + return GameEntry.PlayerInventory.TryAddParticipantTower(towerItemId, MaxParticipantCount); } public bool TryRemoveParticipantTower(long towerItemId) { + if (_state == RepoFormState.Sell) + { + return false; + } + if (GameEntry.PlayerInventory == null) { BackpackInventoryData fallbackInventory = GetOrCreateFallbackInventory(); @@ -61,12 +90,120 @@ namespace GeometryTD.UI return GameEntry.PlayerInventory.TryRemoveParticipantTower(towerItemId); } + public RepoFormRawData EnterSellState() + { + _state = RepoFormState.Sell; + _selectedSellItemIds.Clear(); + return CreateInitialModel(); + } + + public RepoFormRawData ExitSellState() + { + _state = RepoFormState.Assemble; + _selectedSellItemIds.Clear(); + return CreateInitialModel(); + } + + public bool TryToggleSellSelection( + long itemId, + out RepoFormRawData rawData, + out PlayerInventorySaleFailureReason failureReason) + { + rawData = null; + failureReason = PlayerInventorySaleFailureReason.None; + if (_state != RepoFormState.Sell || itemId <= 0 || GameEntry.PlayerInventory == null) + { + failureReason = PlayerInventorySaleFailureReason.InvalidSelection; + return false; + } + + if (_selectedSellItemIds.Contains(itemId)) + { + _selectedSellItemIds.Remove(itemId); + rawData = CreateInitialModel(); + return true; + } + + if (!GameEntry.PlayerInventory.TryGetSaleCandidate(itemId, out PlayerInventorySaleCandidate candidate) || + candidate == null) + { + failureReason = PlayerInventorySaleFailureReason.ItemNotFound; + return false; + } + + if (!candidate.IsSellable) + { + failureReason = candidate.FailureReason; + return false; + } + + _selectedSellItemIds.Add(itemId); + rawData = CreateInitialModel(); + return true; + } + + public bool TryConfirmSellSelection(out RepoFormRawData rawData, out PlayerInventorySaleResult result) + { + rawData = null; + result = null; + if (_state != RepoFormState.Sell || _selectedSellItemIds.Count <= 0 || GameEntry.PlayerInventory == null) + { + return false; + } + + if (!GameEntry.PlayerInventory.TrySellItems(_selectedSellItemIds, out PlayerInventorySaleResult saleResult) || + saleResult == null || + !saleResult.IsSuccess) + { + result = saleResult; + rawData = CreateInitialModel(); + return false; + } + + _selectedSellItemIds.Clear(); + result = saleResult; + rawData = CreateInitialModel(); + return true; + } + private BackpackInventoryData GetOrCreateFallbackInventory() { _fallbackInventory ??= InventorySeedUtility.CreateSampleInventory(); InventoryParticipantUtility.NormalizeParticipantState(_fallbackInventory, MaxParticipantCount); return _fallbackInventory; } + + private long[] BuildSelectedSellItemArray() + { + if (_selectedSellItemIds.Count <= 0) + { + return System.Array.Empty(); + } + + long[] itemIds = new long[_selectedSellItemIds.Count]; + _selectedSellItemIds.CopyTo(itemIds); + return itemIds; + } + + private int ResolveSelectedSellTotalPrice() + { + if (_state != RepoFormState.Sell || _selectedSellItemIds.Count <= 0 || GameEntry.PlayerInventory == null) + { + return 0; + } + + int total = 0; + foreach (long itemId in _selectedSellItemIds) + { + if (GameEntry.PlayerInventory.TryGetSaleCandidate(itemId, out PlayerInventorySaleCandidate candidate) && + candidate != null && + candidate.IsSellable) + { + total += candidate.Price; + } + } + + return total; + } } } - diff --git a/Assets/GameMain/Scripts/UI/Game/View/CombineArea.cs b/Assets/GameMain/Scripts/UI/Game/View/CombineArea.cs index 195510d..6a29d81 100644 --- a/Assets/GameMain/Scripts/UI/Game/View/CombineArea.cs +++ b/Assets/GameMain/Scripts/UI/Game/View/CombineArea.cs @@ -8,6 +8,7 @@ namespace GeometryTD.UI public class CombineArea : MonoBehaviour, IDropHandler { [SerializeField] private CombineSlotItem[] _slots; + private bool _isInteractionEnabled = true; public void OnInit(CombineAreaContext context) { @@ -43,6 +44,11 @@ namespace GeometryTD.UI public void OnCombineClick() { + if (!_isInteractionEnabled) + { + return; + } + if (!TryGetBoundItemId(TowerCompSlotType.Muzzle, out long muzzleItemId) || !TryGetBoundItemId(TowerCompSlotType.Bearing, out long bearingItemId) || !TryGetBoundItemId(TowerCompSlotType.Base, out long baseItemId)) @@ -55,6 +61,11 @@ namespace GeometryTD.UI public bool TryAssignItem(RepoItemContext itemContext) { + if (!_isInteractionEnabled) + { + return false; + } + if (itemContext == null || itemContext.ComponentSlotType == TowerCompSlotType.None) { return false; @@ -91,6 +102,11 @@ namespace GeometryTD.UI public void OnDrop(PointerEventData eventData) { + if (!_isInteractionEnabled) + { + return; + } + if (eventData == null) { return; @@ -113,6 +129,11 @@ namespace GeometryTD.UI dragItem.SetDropResult(assigned); } + public void SetInteractionEnabled(bool enabled) + { + _isInteractionEnabled = enabled; + } + private CombineSlotItem FindSlot(TowerCompSlotType slotType) { if (_slots == null) diff --git a/Assets/GameMain/Scripts/UI/Game/View/ParticipantArea.cs b/Assets/GameMain/Scripts/UI/Game/View/ParticipantArea.cs index a910399..cfe0c81 100644 --- a/Assets/GameMain/Scripts/UI/Game/View/ParticipantArea.cs +++ b/Assets/GameMain/Scripts/UI/Game/View/ParticipantArea.cs @@ -14,6 +14,7 @@ namespace GeometryTD.UI [SerializeField] private Transform _content; [SerializeField] private TowerRepoItem _towerItemTemplate; [SerializeField] private int _instancePoolCapacity = 8; + private bool _isInteractionEnabled = true; private readonly List _activeItems = new List(); private readonly HashSet _boundItemIds = new HashSet(); @@ -81,6 +82,11 @@ namespace GeometryTD.UI public bool CanAssign(IRepoDragItemView dragItem) { + if (!_isInteractionEnabled) + { + return false; + } + if (dragItem == null || dragItem.InstanceId <= 0) { return false; @@ -106,6 +112,11 @@ namespace GeometryTD.UI public void OnDrop(PointerEventData eventData) { + if (!_isInteractionEnabled) + { + return; + } + if (eventData == null) { return; @@ -134,6 +145,11 @@ namespace GeometryTD.UI GameEntry.Event.Fire(this, RepoParticipantAssignRequestedEventArgs.Create(dragItem.InstanceId)); } + public void SetInteractionEnabled(bool enabled) + { + _isInteractionEnabled = enabled; + } + private void EnsurePool() { if (_itemPool != null) diff --git a/Assets/GameMain/Scripts/UI/Game/View/RepoForm.cs b/Assets/GameMain/Scripts/UI/Game/View/RepoForm.cs index 770dbaf..2cb19bd 100644 --- a/Assets/GameMain/Scripts/UI/Game/View/RepoForm.cs +++ b/Assets/GameMain/Scripts/UI/Game/View/RepoForm.cs @@ -1,6 +1,7 @@ using GeometryTD.CustomEvent; using TMPro; using UnityEngine; +using UnityEngine.UI; using UnityGameFramework.Runtime; namespace GeometryTD.UI @@ -8,12 +9,12 @@ namespace GeometryTD.UI public class RepoForm : UGuiForm { [SerializeField] private CombineArea _combineArea; - [SerializeField] private CompArea _compArea; - + [SerializeField] private SellArea _sellArea; [SerializeField] private ParticipantArea _participantArea; - [SerializeField] private TMP_Text _goldText; + [SerializeField] private CommonButton _sellModeButton; + [SerializeField] private TMP_Text _sellModeButtonText; public void RefreshUI(RepoFormContext context) { @@ -23,10 +24,25 @@ namespace GeometryTD.UI } RefreshGoldText(context.GoldText); + RefreshStateUI(context); _combineArea?.OnInit(context.CombineAreaContext); _compArea?.OnInit(context.CompAreaContext); + _sellArea?.OnInit(context.SellAreaContext); _participantArea?.OnInit(context.ParticipantAreaContext); + + if (_combineArea != null) + { + _combineArea.gameObject.SetActive(context.ShowCombineArea); + _combineArea.SetInteractionEnabled(context.State == RepoFormState.Assemble); + } + + if (_sellArea != null) + { + _sellArea.gameObject.SetActive(context.ShowSellArea); + } + + _participantArea?.SetInteractionEnabled(context.State == RepoFormState.Assemble); } public void RefreshGoldText(string goldText) @@ -89,6 +105,7 @@ namespace GeometryTD.UI _combineArea?.OnReset(); _compArea?.OnReset(); + _sellArea?.OnReset(); _participantArea?.OnReset(); base.OnClose(isShutdown, userData); } @@ -97,5 +114,23 @@ namespace GeometryTD.UI { GameEntry.Event.Fire(this, RepoFormReturnEventArgs.Create()); } + + public void OnSellModeButtonClick() + { + GameEntry.Event.Fire(this, RepoSellModeToggleRequestedEventArgs.Create()); + } + + private void RefreshStateUI(RepoFormContext context) + { + if (_sellModeButton != null) + { + _sellModeButton.gameObject.SetActive(context != null && context.ShowSellModeButton); + } + + if (_sellModeButtonText != null) + { + _sellModeButtonText.text = context?.SellModeButtonText ?? "出售模式"; + } + } } } diff --git a/Assets/GameMain/Scripts/UI/Game/View/RepoItem.cs b/Assets/GameMain/Scripts/UI/Game/View/RepoItem.cs index e63ad3d..3f0f562 100644 --- a/Assets/GameMain/Scripts/UI/Game/View/RepoItem.cs +++ b/Assets/GameMain/Scripts/UI/Game/View/RepoItem.cs @@ -13,6 +13,7 @@ namespace GeometryTD.UI [SerializeField] private IconArea _iconArea; private static readonly Color SelectedColor = new Color32(255, 216, 102, 255); + private static readonly Color DisabledSellColor = new Color32(96, 96, 96, 255); private static readonly Color EmptyEnduranceColor = new Color(0.8f, 0f, 0f, 1f); private static readonly Color FullEnduranceColor = new Color(0f, 0.8f, 0f, 1f); private static readonly Vector2 DefaultDragGhostSize = new Vector2(64f, 64f); @@ -72,7 +73,7 @@ namespace GeometryTD.UI _context = context; _iconArea.OnInit(context.IconAreaContext); - SetSelected(false); + SetSelected(context.IsSellSelected); ResetDragState(); } @@ -104,6 +105,12 @@ namespace GeometryTD.UI return; } + if (_context != null && _context.IsSellMode && !_context.IsSellable) + { + _bgImage.color = DisabledSellColor; + return; + } + float enduranceRate = _context != null ? Mathf.Clamp01(_context.EnduranceRate01) : 1f; _bgImage.color = Color.Lerp(EmptyEnduranceColor, FullEnduranceColor, enduranceRate); } @@ -212,6 +219,11 @@ namespace GeometryTD.UI return false; } + if (_context.IsSellMode) + { + return false; + } + if (_isSelected && _context.ComponentSlotType != TowerCompSlotType.None) { return false; diff --git a/Assets/GameMain/Scripts/UI/Game/View/SellArea.cs b/Assets/GameMain/Scripts/UI/Game/View/SellArea.cs new file mode 100644 index 0000000..688f46f --- /dev/null +++ b/Assets/GameMain/Scripts/UI/Game/View/SellArea.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using GeometryTD.CustomEvent; +using TMPro; +using UnityEngine; + +namespace GeometryTD.UI +{ + public class SellArea : MonoBehaviour + { + [SerializeField] private Transform _content; + [SerializeField] private RepoItem _itemTemplate; + [SerializeField] private TowerRepoItem _towerItemTemplate; + [SerializeField] private TMP_Text _totalPrice; + + private readonly List _activeComponentItems = new List(); + private readonly List _activeTowerItems = new List(); + private SellAreaContext _context; + + public void OnInit(SellAreaContext context) + { + OnReset(); + _context = context; + + if (_totalPrice != null) + { + _totalPrice.text = context?.TotalPriceText ?? string.Empty; + } + + if (context == null || _content == null) + { + return; + } + + if (context.ComponentItems != null) + { + foreach (RepoItemContext itemContext in context.ComponentItems) + { + if (itemContext == null || _itemTemplate == null) + { + continue; + } + + RepoItem item = Instantiate(_itemTemplate, _content); + item.gameObject.SetActive(true); + item.OnInit(itemContext); + _activeComponentItems.Add(item); + } + } + + if (context.TowerItems != null) + { + foreach (TowerRepoItemContext itemContext in context.TowerItems) + { + if (itemContext == null || _towerItemTemplate == null) + { + continue; + } + + TowerRepoItem item = Instantiate(_towerItemTemplate, _content); + item.gameObject.SetActive(true); + item.OnInit(itemContext); + _activeTowerItems.Add(item); + } + } + } + + public void OnReset() + { + for (int i = _activeComponentItems.Count - 1; i >= 0; i--) + { + RepoItem item = _activeComponentItems[i]; + if (item != null) + { + Destroy(item.gameObject); + } + } + + for (int i = _activeTowerItems.Count - 1; i >= 0; i--) + { + TowerRepoItem item = _activeTowerItems[i]; + if (item != null) + { + Destroy(item.gameObject); + } + } + + _activeComponentItems.Clear(); + _activeTowerItems.Clear(); + _context = null; + + if (_totalPrice != null) + { + _totalPrice.text = string.Empty; + } + } + + public void OnCancelButtonClick() + { + GameEntry.Event.Fire(this, RepoSellCancelRequestedEventArgs.Create()); + } + + public void OnConfirmButtonClick() + { + if (_context == null || !_context.CanConfirmSell) + { + return; + } + + GameEntry.Event.Fire(this, RepoSellConfirmRequestedEventArgs.Create()); + } + } +} diff --git a/Assets/GameMain/Scripts/UI/Game/View/SellArea.cs.meta b/Assets/GameMain/Scripts/UI/Game/View/SellArea.cs.meta new file mode 100644 index 0000000..66d3595 --- /dev/null +++ b/Assets/GameMain/Scripts/UI/Game/View/SellArea.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 548fcad4dc8202f41b67f48aab7bed13 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/UI/Shop/UseCase/ShopFormUseCase.cs b/Assets/GameMain/Scripts/UI/Shop/UseCase/ShopFormUseCase.cs index 04b73e9..6f86eba 100644 --- a/Assets/GameMain/Scripts/UI/Shop/UseCase/ShopFormUseCase.cs +++ b/Assets/GameMain/Scripts/UI/Shop/UseCase/ShopFormUseCase.cs @@ -61,37 +61,16 @@ namespace GeometryTD.UI return false; } - if (!GameEntry.PlayerInventory.TryConsumeGold(goodsItem.Price)) + if (!GameEntry.PlayerInventory.TryPurchaseComponent(goodsItem.SourceItem, goodsItem.Price)) { - Log.Warning("ShopFormUseCase.TryPurchase() failed. Not enough gold for goods item {0}.", goodsIndex); + Log.Warning("ShopFormUseCase.TryPurchase() failed. Purchase command was rejected for goods item {0}.", goodsIndex); return false; } - BackpackInventoryData inventoryDelta = WrapSingleItem(goodsItem.SourceItem); - GameEntry.PlayerInventory.MergeInventory(inventoryDelta); goodsItem.IsPurchased = true; updatedRawData = CreateInitialModel(); return true; } - private static BackpackInventoryData WrapSingleItem(TowerCompItemData item) - { - BackpackInventoryData inventory = new BackpackInventoryData(); - switch (item) - { - case MuzzleCompItemData muzzleComp: - inventory.MuzzleComponents.Add(InventoryCloneUtility.CloneMuzzleComp(muzzleComp)); - break; - case BearingCompItemData bearingComp: - inventory.BearingComponents.Add(InventoryCloneUtility.CloneBearingComp(bearingComp)); - break; - case BaseCompItemData baseComp: - inventory.BaseComponents.Add(InventoryCloneUtility.CloneBaseComp(baseComp)); - break; - } - - return inventory; - } - } } diff --git a/Assets/GameMain/Scripts/Utility/ShopPriceRuleService.cs b/Assets/GameMain/Scripts/Utility/ShopPriceRuleService.cs new file mode 100644 index 0000000..69ec2b2 --- /dev/null +++ b/Assets/GameMain/Scripts/Utility/ShopPriceRuleService.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using GameFramework.DataTable; +using GeometryTD.DataTable; +using GeometryTD.Definition; +using UnityEngine; +using Random = System.Random; + +namespace GeometryTD.CustomUtility +{ + public static class ShopPriceRuleService + { + public static int ResolveRandomBuyPrice(IReadOnlyList shopPriceRows, RarityType rarity, Random random) + { + if (!TryFindPriceRow(shopPriceRows, rarity, out DRShopPrice row) || row == null) + { + return 0; + } + + int min = Mathf.Max(0, row.MinPrice); + int max = Mathf.Max(min, row.MaxPrice); + return random != null ? random.Next(min, max + 1) : min; + } + + public static int ResolveComponentSalePrice(TowerCompItemData component, IDataTable shopPriceTable = null) + { + if (component == null) + { + return 0; + } + + return ResolveBasePrice(component.Rarity, shopPriceTable); + } + + public static bool TryResolveTowerSalePrice( + TowerItemData tower, + BackpackInventoryData inventory, + out int price, + IDataTable shopPriceTable = null) + { + price = 0; + if (tower == null || inventory == null) + { + return false; + } + + if (!TryGetComponentById(inventory.MuzzleComponents, tower.MuzzleComponentInstanceId, out MuzzleCompItemData muzzleComp) || + !TryGetComponentById(inventory.BearingComponents, tower.BearingComponentInstanceId, out BearingCompItemData bearingComp) || + !TryGetComponentById(inventory.BaseComponents, tower.BaseComponentInstanceId, out BaseCompItemData baseComp)) + { + return false; + } + + price = ResolveComponentSalePrice(muzzleComp, shopPriceTable) + + ResolveComponentSalePrice(bearingComp, shopPriceTable) + + ResolveComponentSalePrice(baseComp, shopPriceTable); + return price > 0; + } + + public static int ResolveBasePrice(RarityType rarity, IDataTable shopPriceTable = null) + { + IDataTable resolvedTable = shopPriceTable ?? GameEntry.DataTable.GetDataTable(); + if (resolvedTable == null) + { + return 0; + } + + DRShopPrice[] rows = resolvedTable.GetAllDataRows(); + if (!TryFindPriceRow(rows, rarity, out DRShopPrice row) || row == null) + { + return 0; + } + + int min = Mathf.Max(0, row.MinPrice); + int max = Mathf.Max(min, row.MaxPrice); + return Mathf.RoundToInt((min + max) * 0.5f); + } + + private static bool TryFindPriceRow(IReadOnlyList rows, RarityType rarity, out DRShopPrice result) + { + result = null; + if (rows == null) + { + return false; + } + + for (int i = 0; i < rows.Count; i++) + { + DRShopPrice row = rows[i]; + if (row != null && row.Rarity == rarity) + { + result = row; + return true; + } + } + + return false; + } + + private static bool TryGetComponentById(IReadOnlyList components, long instanceId, out TComp result) + where TComp : TowerCompItemData + { + result = null; + if (components == null || instanceId <= 0) + { + return false; + } + + for (int i = 0; i < components.Count; i++) + { + TComp component = components[i]; + if (component != null && component.InstanceId == instanceId) + { + result = component; + return true; + } + } + + return false; + } + } +} diff --git a/Assets/GameMain/Scripts/Utility/ShopPriceRuleService.cs.meta b/Assets/GameMain/Scripts/Utility/ShopPriceRuleService.cs.meta new file mode 100644 index 0000000..cb89519 --- /dev/null +++ b/Assets/GameMain/Scripts/Utility/ShopPriceRuleService.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b1bcf5c75bea4faaacdb4ffc5adfaf36 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/UI/UIForms/RepoForm.prefab b/Assets/GameMain/UI/UIForms/RepoForm.prefab index 89d0b9b..03bace1 100644 --- a/Assets/GameMain/UI/UIForms/RepoForm.prefab +++ b/Assets/GameMain/UI/UIForms/RepoForm.prefab @@ -55,6 +55,267 @@ MonoBehaviour: _towerItemTemplate: {fileID: 4730609612906943122, guid: 81bda3e30070fa34884bafbe9a9c72f9, type: 3} _instancePoolCapacity: 64 +--- !u!1 &907933136233057619 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4279807702350963152} + - component: {fileID: 5496391158123026093} + - component: {fileID: 184327136926035885} + m_Layer: 5 + m_Name: TotalPrice + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4279807702350963152 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 907933136233057619} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8348982325672454279} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 150, y: 320} + m_SizeDelta: {x: 800, y: 100} + m_Pivot: {x: 0, y: 0.5} +--- !u!222 &5496391158123026093 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 907933136233057619} + m_CullTransparentMesh: 1 +--- !u!114 &184327136926035885 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 907933136233057619} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: "\u603B\u4EF7\u503C\uFF1A12344444" + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 99d811b0183246646a2ce8df996f4bca, type: 2} + m_sharedMaterial: {fileID: -1106088975554028259, guid: 99d811b0183246646a2ce8df996f4bca, + type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 60 + m_fontSizeBase: 60 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!1 &1060599201473345598 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4292006764208555715} + - component: {fileID: 3766674013608514801} + - component: {fileID: 4383728872725652712} + - component: {fileID: 1288348593126858729} + m_Layer: 5 + m_Name: Viewport + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4292006764208555715 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1060599201473345598} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 5957827636872393984} + m_Father: {fileID: 9047709247529434291} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -17, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!222 &3766674013608514801 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1060599201473345598} + m_CullTransparentMesh: 1 +--- !u!114 &4383728872725652712 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1060599201473345598} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10917, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &1288348593126858729 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1060599201473345598} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ShowMaskGraphic: 0 +--- !u!1 &1111733066985133635 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3629555787076626876} + m_Layer: 5 + m_Name: Sliding Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3629555787076626876 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1111733066985133635} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2261290593395169801} + m_Father: {fileID: 2774774990097597208} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: -20} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &2380285751925872027 GameObject: m_ObjectHideFlags: 0 @@ -513,8 +774,11 @@ MonoBehaviour: m_EditorClassIdentifier: _combineArea: {fileID: 2687805356437946940} _compArea: {fileID: 370202498391855143} + _sellArea: {fileID: 6060020296434180814} _participantArea: {fileID: 6217930899804600847} _goldText: {fileID: 661567130228843717} + _sellModeButton: {fileID: 3349081532656245430} + _sellModeButtonText: {fileID: 914007900409045589} --- !u!1 &4000410608128749255 GameObject: m_ObjectHideFlags: 0 @@ -659,6 +923,81 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: -20, y: -20} m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &4465816672473083689 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5957827636872393984} + - component: {fileID: 857499875500161765} + - component: {fileID: 6019838421769854178} + m_Layer: 5 + m_Name: Content + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5957827636872393984 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4465816672473083689} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4292006764208555715} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -33, y: 10} + m_Pivot: {x: 0, y: 1} +--- !u!114 &857499875500161765 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4465816672473083689} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8a8695521f0d02e499659fee002a26c2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 60 + m_Right: 30 + m_Top: 30 + m_Bottom: 0 + m_ChildAlignment: 0 + m_StartCorner: 0 + m_StartAxis: 0 + m_CellSize: {x: 160, y: 160} + m_Spacing: {x: 20, y: 20} + m_Constraint: 1 + m_ConstraintCount: 6 +--- !u!114 &6019838421769854178 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4465816672473083689} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalFit: 2 + m_VerticalFit: 2 --- !u!1 &4950945784006183915 GameObject: m_ObjectHideFlags: 0 @@ -1072,6 +1411,240 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_ShowMaskGraphic: 0 +--- !u!1 &6203040413718650769 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2774774990097597208} + - component: {fileID: 795531778692392471} + - component: {fileID: 4399796340704300239} + - component: {fileID: 3040717564857391564} + m_Layer: 5 + m_Name: Scrollbar Vertical + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2774774990097597208 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6203040413718650769} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 3629555787076626876} + m_Father: {fileID: 9047709247529434291} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: -17} + m_Pivot: {x: 1, y: 1} +--- !u!222 &795531778692392471 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6203040413718650769} + m_CullTransparentMesh: 1 +--- !u!114 &4399796340704300239 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6203040413718650769} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &3040717564857391564 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6203040413718650769} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 3850274832229165113} + m_HandleRect: {fileID: 2261290593395169801} + m_Direction: 2 + m_Value: 0 + m_Size: 1 + m_NumberOfSteps: 0 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &7130819799218509777 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9047709247529434291} + - component: {fileID: 9092085375754682417} + - component: {fileID: 3688455314932519904} + - component: {fileID: 202843227589414132} + m_Layer: 5 + m_Name: SellList + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &9047709247529434291 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7130819799218509777} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 4292006764208555715} + - {fileID: 2774774990097597208} + m_Father: {fileID: 8348982325672454279} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 105} + m_SizeDelta: {x: 1200, y: 710} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &9092085375754682417 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7130819799218509777} + m_CullTransparentMesh: 1 +--- !u!114 &3688455314932519904 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7130819799218509777} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0.392} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &202843227589414132 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7130819799218509777} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Content: {fileID: 1805918912036091081} + m_Horizontal: 0 + m_Vertical: 1 + m_MovementType: 1 + m_Elasticity: 0 + m_Inertia: 1 + m_DecelerationRate: 0.135 + m_ScrollSensitivity: 1 + m_Viewport: {fileID: 4292006764208555715} + m_HorizontalScrollbar: {fileID: 0} + m_VerticalScrollbar: {fileID: 3040717564857391564} + m_HorizontalScrollbarVisibility: 2 + m_VerticalScrollbarVisibility: 2 + m_HorizontalScrollbarSpacing: -3 + m_VerticalScrollbarSpacing: -3 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] --- !u!1 &7193358579279646244 GameObject: m_ObjectHideFlags: 0 @@ -1103,7 +1676,9 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 3177067923545511236} + - {fileID: 2908471288600536575} - {fileID: 437137211665287909} + - {fileID: 8348982325672454279} - {fileID: 4661661401541872014} - {fileID: 6194629833245286025} - {fileID: 956737910225412855} @@ -1152,6 +1727,105 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 +--- !u!1 &7287044292000371376 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8348982325672454279} + - component: {fileID: 6789869904947446289} + - component: {fileID: 6060020296434180814} + - component: {fileID: 2422864306540819095} + m_Layer: 5 + m_Name: SellArea + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &8348982325672454279 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7287044292000371376} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 99683471705577092} + - {fileID: 3093710773949036846} + - {fileID: 9047709247529434291} + - {fileID: 337860180342543876} + - {fileID: 4279807702350963152} + m_Father: {fileID: 948419798348630685} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 700, y: 0} + m_SizeDelta: {x: 1200, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6789869904947446289 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7287044292000371376} + m_CullTransparentMesh: 1 +--- !u!114 &6060020296434180814 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7287044292000371376} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 548fcad4dc8202f41b67f48aab7bed13, type: 3} + m_Name: + m_EditorClassIdentifier: + _content: {fileID: 5957827636872393984} + _itemTemplate: {fileID: 8394974685918372820, guid: 2ead8e403e355dd4b9296a9d0a871a83, + type: 3} + _towerItemTemplate: {fileID: 4730609612906943122, guid: 81bda3e30070fa34884bafbe9a9c72f9, + type: 3} + _totalPrice: {fileID: 184327136926035885} +--- !u!114 &2422864306540819095 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7287044292000371376} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!1 &7765143715849586970 GameObject: m_ObjectHideFlags: 0 @@ -1263,6 +1937,216 @@ RectTransform: m_AnchoredPosition: {x: -1000, y: -100} m_SizeDelta: {x: 400, y: 80} m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &8646734606169868733 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2261290593395169801} + - component: {fileID: 6073230855543914652} + - component: {fileID: 3850274832229165113} + m_Layer: 5 + m_Name: Handle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2261290593395169801 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8646734606169868733} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 3629555787076626876} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6073230855543914652 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8646734606169868733} + m_CullTransparentMesh: 1 +--- !u!114 &3850274832229165113 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8646734606169868733} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &8746145092071900367 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 337860180342543876} + - component: {fileID: 4352850869758593843} + - component: {fileID: 4219933001661327060} + m_Layer: 5 + m_Name: TitleText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &337860180342543876 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8746145092071900367} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8348982325672454279} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 1} + m_AnchorMax: {x: 0.5, y: 1} + m_AnchoredPosition: {x: 0, y: -100} + m_SizeDelta: {x: 1200, y: 120} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4352850869758593843 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8746145092071900367} + m_CullTransparentMesh: 1 +--- !u!114 &4219933001661327060 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8746145092071900367} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: "\u9009\u62E9\u8981\u51FA\u552E\u7684\u7EC4\u4EF6/\u9632\u5FA1\u5854" + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 99d811b0183246646a2ce8df996f4bca, type: 2} + m_sharedMaterial: {fileID: -1106088975554028259, guid: 99d811b0183246646a2ce8df996f4bca, + type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 80 + m_fontSizeBase: 80 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} --- !u!1001 &19160693396753940 PrefabInstance: m_ObjectHideFlags: 0 @@ -1944,7 +2828,7 @@ PrefabInstance: - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, type: 3} propertyPath: m_SizeDelta.x - value: 400 + value: 300 objectReference: {fileID: 0} - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, type: 3} @@ -1989,7 +2873,7 @@ PrefabInstance: - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, type: 3} propertyPath: m_AnchoredPosition.x - value: -400 + value: -200 objectReference: {fileID: 0} - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, type: 3} @@ -2022,6 +2906,492 @@ RectTransform: type: 3} m_PrefabInstance: {fileID: 1316095819115369395} m_PrefabAsset: {fileID: 0} +--- !u!1001 &1493948041014525913 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 8348982325672454279} + m_Modifications: + - target: {fileID: 770565994539545022, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_Name + value: ConfirmButton + objectReference: {fileID: 0} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_text + value: "\u51FA\u552E" + objectReference: {fileID: 0} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_fontSize + value: 45 + objectReference: {fileID: 0} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_fontAsset + value: + objectReference: {fileID: 11400000, guid: 99d811b0183246646a2ce8df996f4bca, + type: 2} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_fontSizeBase + value: 45 + objectReference: {fileID: 0} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_sharedMaterial + value: + objectReference: {fileID: -1106088975554028259, guid: 99d811b0183246646a2ce8df996f4bca, + type: 2} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_hasFontAssetChanged + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_Mode + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[0].m_Target + value: + objectReference: {fileID: 207314004719697847} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_Target + value: + objectReference: {fileID: 6060020296434180814} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onHover.m_PersistentCalls.m_Calls.Array.data[0].m_Target + value: + objectReference: {fileID: 207314004719697847} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_MethodName + value: OnConfirmButtonClick + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_TargetAssemblyTypeName + value: GeometryTD.UI.SellArea, GeometryTD.Runtime + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_Arguments.m_ObjectArgumentAssemblyTypeName + value: UnityEngine.Object, UnityEngine + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchorMax.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchorMax.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchorMin.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchorMin.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_SizeDelta.x + value: 350 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_SizeDelta.y + value: 80 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 250 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -500 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 2307f223279813546a43b221ddd496cc, type: 3} +--- !u!224 &3093710773949036846 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + m_PrefabInstance: {fileID: 1493948041014525913} + m_PrefabAsset: {fileID: 0} +--- !u!1001 &1587650153232032520 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 948419798348630685} + m_Modifications: + - target: {fileID: 770565994539545022, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_Name + value: SellButton + objectReference: {fileID: 0} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_text + value: "\u51FA\u552E" + objectReference: {fileID: 0} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_fontSize + value: 45 + objectReference: {fileID: 0} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_fontAsset + value: + objectReference: {fileID: 11400000, guid: 99d811b0183246646a2ce8df996f4bca, + type: 2} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_fontStyle + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_fontSizeBase + value: 45 + objectReference: {fileID: 0} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_sharedMaterial + value: + objectReference: {fileID: -1106088975554028259, guid: 99d811b0183246646a2ce8df996f4bca, + type: 2} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_hasFontAssetChanged + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.size + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onHover.m_PersistentCalls.m_Calls.Array.size + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[0].m_Mode + value: 3 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_Mode + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onHover.m_PersistentCalls.m_Calls.Array.data[0].m_Mode + value: 3 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[0].m_Target + value: + objectReference: {fileID: 207314004719697847} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_Target + value: + objectReference: {fileID: 207314004719697847} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onHover.m_PersistentCalls.m_Calls.Array.data[0].m_Target + value: + objectReference: {fileID: 207314004719697847} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[0].m_CallState + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_CallState + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onHover.m_PersistentCalls.m_Calls.Array.data[0].m_CallState + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[0].m_MethodName + value: PlayUISound + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_MethodName + value: OnSellModeButtonClick + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onHover.m_PersistentCalls.m_Calls.Array.data[0].m_MethodName + value: PlayUISound + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[0].m_TargetAssemblyTypeName + value: GeometryTD.UI.UGuiForm, Assembly-CSharp + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_TargetAssemblyTypeName + value: GeometryTD.UI.RepoForm, GeometryTD.Runtime + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onHover.m_PersistentCalls.m_Calls.Array.data[0].m_TargetAssemblyTypeName + value: GeometryTD.UI.UGuiForm, Assembly-CSharp + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[0].m_Arguments.m_IntArgument + value: 10001 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onHover.m_PersistentCalls.m_Calls.Array.data[0].m_Arguments.m_IntArgument + value: 10000 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[0].m_Arguments.m_ObjectArgumentAssemblyTypeName + value: UnityEngine.Object, UnityEngine + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_Arguments.m_ObjectArgumentAssemblyTypeName + value: UnityEngine.Object, UnityEngine + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onHover.m_PersistentCalls.m_Calls.Array.data[0].m_Arguments.m_ObjectArgumentAssemblyTypeName + value: UnityEngine.Object, UnityEngine + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchorMax.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchorMax.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchorMin.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchorMin.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_SizeDelta.x + value: 300 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_SizeDelta.y + value: 80 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchoredPosition.x + value: -600 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -100 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 2307f223279813546a43b221ddd496cc, type: 3} +--- !u!114 &914007900409045589 stripped +MonoBehaviour: + m_CorrespondingSourceObject: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + m_PrefabInstance: {fileID: 1587650153232032520} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!224 &2908471288600536575 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + m_PrefabInstance: {fileID: 1587650153232032520} + m_PrefabAsset: {fileID: 0} +--- !u!114 &3349081532656245430 stripped +MonoBehaviour: + m_CorrespondingSourceObject: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + m_PrefabInstance: {fileID: 1587650153232032520} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a5079836f95c2a44b96fa331487ebb70, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1001 &3783443560733274561 PrefabInstance: m_ObjectHideFlags: 0 @@ -2389,6 +3759,197 @@ RectTransform: type: 3} m_PrefabInstance: {fileID: 4121104612746912152} m_PrefabAsset: {fileID: 0} +--- !u!1001 &4555010368161009779 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 8348982325672454279} + m_Modifications: + - target: {fileID: 770565994539545022, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_Name + value: CancelButton + objectReference: {fileID: 0} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_text + value: "\u53D6\u6D88" + objectReference: {fileID: 0} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_fontSize + value: 45 + objectReference: {fileID: 0} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_fontAsset + value: + objectReference: {fileID: 11400000, guid: 99d811b0183246646a2ce8df996f4bca, + type: 2} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_fontSizeBase + value: 45 + objectReference: {fileID: 0} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_sharedMaterial + value: + objectReference: {fileID: -1106088975554028259, guid: 99d811b0183246646a2ce8df996f4bca, + type: 2} + - target: {fileID: 1920576543566152029, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_hasFontAssetChanged + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_Mode + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[0].m_Target + value: + objectReference: {fileID: 207314004719697847} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_Target + value: + objectReference: {fileID: 6060020296434180814} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onHover.m_PersistentCalls.m_Calls.Array.data[0].m_Target + value: + objectReference: {fileID: 207314004719697847} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_MethodName + value: OnCancelButtonClick + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_TargetAssemblyTypeName + value: GeometryTD.UI.SellArea, GeometryTD.Runtime + objectReference: {fileID: 0} + - target: {fileID: 4067353614215461310, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: _onClick.m_PersistentCalls.m_Calls.Array.data[1].m_Arguments.m_ObjectArgumentAssemblyTypeName + value: UnityEngine.Object, UnityEngine + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchorMax.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchorMax.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchorMin.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchorMin.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_SizeDelta.x + value: 350 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_SizeDelta.y + value: 80 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchoredPosition.x + value: -250 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -500 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 2307f223279813546a43b221ddd496cc, type: 3} +--- !u!224 &99683471705577092 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4491355866364659447, guid: 2307f223279813546a43b221ddd496cc, + type: 3} + m_PrefabInstance: {fileID: 4555010368161009779} + m_PrefabAsset: {fileID: 0} --- !u!1001 &4883439655870296316 PrefabInstance: m_ObjectHideFlags: 0 diff --git a/Assets/Tests/EditMode/PlayerInventoryTradeServiceTests.cs b/Assets/Tests/EditMode/PlayerInventoryTradeServiceTests.cs new file mode 100644 index 0000000..ee097c7 --- /dev/null +++ b/Assets/Tests/EditMode/PlayerInventoryTradeServiceTests.cs @@ -0,0 +1,298 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using GameFramework.DataTable; +using GeometryTD.CustomComponent; +using GeometryTD.DataTable; +using GeometryTD.Definition; +using NUnit.Framework; + +namespace GeometryTD.Tests.EditMode +{ + public sealed class PlayerInventoryTradeServiceTests + { + [Test] + public void TrySellItems_Sells_Selected_Component_And_Tower_Atomically() + { + BackpackInventoryData inventory = CreateInventory(); + PlayerInventoryState state = new PlayerInventoryState(); + PlayerInventoryQueryModel queryModel = new PlayerInventoryQueryModel(state); + PlayerInventoryCommandModel commandModel = new PlayerInventoryCommandModel(state); + commandModel.Initialize(inventory, maxParticipantTowerCount: 4); + + PlayerInventoryTradeService tradeService = new PlayerInventoryTradeService( + queryModel, + commandModel, + CreatePriceTable( + CreateShopPriceRow(1, RarityType.Blue, 30, 34), + CreateShopPriceRow(2, RarityType.Purple, 60, 64))); + + bool success = tradeService.TrySellItems(new long[] { 101, 201 }, out PlayerInventorySaleResult result); + + Assert.That(success, Is.True); + Assert.That(result, Is.Not.Null); + Assert.That(result.IsSuccess, Is.True); + Assert.That(result.SoldComponentCount, Is.EqualTo(1)); + Assert.That(result.SoldTowerCount, Is.EqualTo(1)); + Assert.That(queryModel.Inventory.Gold, Is.EqualTo(138)); + Assert.That(queryModel.Inventory.MuzzleComponents.Exists(item => item.InstanceId == 101), Is.False); + Assert.That(queryModel.Inventory.Towers.Exists(item => item.InstanceId == 201), Is.False); + Assert.That(queryModel.Inventory.MuzzleComponents.Exists(item => item.InstanceId == 202), Is.False); + Assert.That(queryModel.Inventory.BearingComponents.Exists(item => item.InstanceId == 203), Is.False); + Assert.That(queryModel.Inventory.BaseComponents.Exists(item => item.InstanceId == 204), Is.False); + } + + [Test] + public void TryGetSaleCandidate_Rejects_Participant_Tower() + { + BackpackInventoryData inventory = CreateInventory(); + inventory.ParticipantTowerInstanceIds.Add(201); + + PlayerInventoryState state = new PlayerInventoryState(); + PlayerInventoryQueryModel queryModel = new PlayerInventoryQueryModel(state); + PlayerInventoryCommandModel commandModel = new PlayerInventoryCommandModel(state); + commandModel.Initialize(inventory, maxParticipantTowerCount: 4); + + PlayerInventoryTradeService tradeService = new PlayerInventoryTradeService( + queryModel, + commandModel, + CreatePriceTable(CreateShopPriceRow(1, RarityType.Blue, 30, 34))); + + bool resolved = tradeService.TryGetSaleCandidate(201, out PlayerInventorySaleCandidate candidate); + + Assert.That(resolved, Is.True); + Assert.That(candidate, Is.Not.Null); + Assert.That(candidate.IsSellable, Is.False); + Assert.That(candidate.FailureReason, Is.EqualTo(PlayerInventorySaleFailureReason.ParticipantTower)); + } + + private static BackpackInventoryData CreateInventory() + { + BackpackInventoryData inventory = new BackpackInventoryData + { + Gold = 10 + }; + + inventory.MuzzleComponents.Add(new MuzzleCompItemData + { + InstanceId = 101, + ConfigId = 1, + Name = "Loose Muzzle", + Rarity = RarityType.Blue, + Endurance = 100f, + IsAssembledIntoTower = false + }); + inventory.MuzzleComponents.Add(new MuzzleCompItemData + { + InstanceId = 202, + ConfigId = 2, + Name = "Tower Muzzle", + Rarity = RarityType.Blue, + Endurance = 100f, + IsAssembledIntoTower = true + }); + inventory.BearingComponents.Add(new BearingCompItemData + { + InstanceId = 203, + ConfigId = 3, + Name = "Tower Bearing", + Rarity = RarityType.Blue, + Endurance = 100f, + IsAssembledIntoTower = true + }); + inventory.BaseComponents.Add(new BaseCompItemData + { + InstanceId = 204, + ConfigId = 4, + Name = "Tower Base", + Rarity = RarityType.Blue, + Endurance = 100f, + IsAssembledIntoTower = true + }); + + inventory.Towers.Add(new TowerItemData + { + InstanceId = 201, + Name = "Tower #201", + Rarity = RarityType.Blue, + MuzzleComponentInstanceId = 202, + BearingComponentInstanceId = 203, + BaseComponentInstanceId = 204 + }); + + return inventory; + } + + private static IDataTable CreatePriceTable(params DRShopPrice[] rows) + { + return new FakeDataTable(rows); + } + + private static DRShopPrice CreateShopPriceRow(int id, RarityType rarity, int minPrice, int maxPrice) + { + DRShopPrice row = new DRShopPrice(); + Assert.That(row.ParseDataRow($"\t{id}\t\t{rarity}\t{minPrice}\t{maxPrice}", null), Is.True); + return row; + } + + private sealed class FakeDataTable : IDataTable where TRow : class, IDataRow + { + private readonly Dictionary _rowsById = new(); + + public FakeDataTable(params TRow[] rows) + { + if (rows == null) + { + return; + } + + for (int i = 0; i < rows.Length; i++) + { + TRow row = rows[i]; + if (row != null) + { + _rowsById[row.Id] = row; + } + } + } + + public string Name => typeof(TRow).Name; + public string FullName => typeof(TRow).FullName; + public Type Type => typeof(TRow); + public int Count => _rowsById.Count; + public TRow this[int id] => GetDataRow(id); + public TRow MinIdDataRow => null; + public TRow MaxIdDataRow => null; + + public bool HasDataRow(int id) => _rowsById.ContainsKey(id); + public bool HasDataRow(Predicate condition) => GetDataRow(condition) != null; + public TRow GetDataRow(int id) => _rowsById.TryGetValue(id, out TRow row) ? row : null; + + public TRow GetDataRow(Predicate condition) + { + foreach (TRow row in _rowsById.Values) + { + if (row != null && condition != null && condition(row)) + { + return row; + } + } + + return null; + } + + public TRow[] GetDataRows(Predicate condition) + { + List results = new(); + GetDataRows(condition, results); + return results.ToArray(); + } + + public void GetDataRows(Predicate condition, List results) + { + results?.Clear(); + if (condition == null || results == null) + { + return; + } + + foreach (TRow row in _rowsById.Values) + { + if (row != null && condition(row)) + { + results.Add(row); + } + } + } + + public TRow[] GetDataRows(Comparison comparison) + { + List results = new(); + GetDataRows(comparison, results); + return results.ToArray(); + } + + public void GetDataRows(Comparison comparison, List results) + { + results?.Clear(); + if (results == null) + { + return; + } + + results.AddRange(_rowsById.Values); + if (comparison != null) + { + results.Sort(comparison); + } + } + + public TRow[] GetDataRows(Predicate condition, Comparison comparison) + { + List results = new(); + GetDataRows(condition, comparison, results); + return results.ToArray(); + } + + public void GetDataRows(Predicate condition, Comparison comparison, List results) + { + GetDataRows(condition, results); + if (results != null && comparison != null) + { + results.Sort(comparison); + } + } + + public TRow[] GetAllDataRows() + { + List results = new(); + GetAllDataRows(results); + return results.ToArray(); + } + + public void GetAllDataRows(List results) + { + results?.Clear(); + if (results == null) + { + return; + } + + foreach (int id in GetOrderedIds()) + { + results.Add(_rowsById[id]); + } + } + + public bool AddDataRow(string dataRowString, object userData) => throw new NotSupportedException(); + public bool AddDataRow(byte[] dataRowBytes, int startIndex, int length, object userData) => throw new NotSupportedException(); + public bool RemoveDataRow(int id) => _rowsById.Remove(id); + + public void RemoveAllDataRows() + { + _rowsById.Clear(); + } + + public IEnumerator GetEnumerator() + { + foreach (int id in GetOrderedIds()) + { + yield return _rowsById[id]; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + private int[] GetOrderedIds() + { + int[] ids = new int[_rowsById.Count]; + _rowsById.Keys.CopyTo(ids, 0); + Array.Sort(ids); + return ids; + } + } + } +} diff --git a/Assets/Tests/EditMode/PlayerInventoryTradeServiceTests.cs.meta b/Assets/Tests/EditMode/PlayerInventoryTradeServiceTests.cs.meta new file mode 100644 index 0000000..9710a53 --- /dev/null +++ b/Assets/Tests/EditMode/PlayerInventoryTradeServiceTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eae86223cf354e758fad4be62efd678d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/docs/CodeX-TODO.md b/docs/CodeX-TODO.md index b1157ff..dec5b0f 100644 --- a/docs/CodeX-TODO.md +++ b/docs/CodeX-TODO.md @@ -90,20 +90,24 @@ - 三个示例事件在局内都能完整触发、结算、回写状态。 - 不会出现组件被扣掉但奖励未发、或耐久扣成负值这种半成功状态。 -### P1-03 商店节点:购买 / 出售组件 +### P1-03 商店节点购买 + 背包出售组件 / 防御塔 -- 核心目标:把当前“仅购买组件”的商店补成完整交易节点。 +- 核心目标:保留商店节点的购买职责,把出售入口迁到背包 `RepoForm`,形成完整交易闭环。 - 交付物: - `Assets/GameMain/Scripts/UI/Shop/` + - `Assets/GameMain/Scripts/UI/Game/` - `Assets/GameMain/Scripts/CustomComponent/ShopNodeComponent.cs` - - 必要时新增 `Assets/GameMain/Scripts/Utility/` 下的交易规则工具 + - `Assets/GameMain/Scripts/CustomComponent/PlayerInventory/` - 具体拆分: - - Shop UI 增加玩家库存展示与出售入口,至少支持出售三类组件。 - - 交易结算统一走一个买卖接口,不要在 UI 层分别修改金币和库存。 + - Shop UI 保持“4 个商品 + 购买 + 打开背包”主链,不再承担库存展示与出售交互。 + - Repo UI 增加明确的出售模式入口;进入后可批量选择未组装组件与非参战防御塔,并显示当前选择总价值。 + - 参战防御塔不能直接出售,必须先从参战区移出;出售整塔时,需要连同其绑定的 3 个组件一起从库存移除。 + - 交易结算统一走库存域的显式买/卖命令,不要在 UI 层分别修改金币和库存。 - 商品购买后状态、库存变化、金币变化要同时刷新,不允许 UI 还显示可买但实际已买。 - 明确商店节点退出条件与节点完成时机,避免出现开店即完成或出售后不回流的问题。 - 验收补充: - - 买卖后库存与金币实时正确更新。 + - 商店购买、背包出售后库存与金币实时正确更新。 + - 从商店里打开背包出售后,返回商店时金币显示与商品状态正确刷新。 - 关闭商店后重新打开同一节点时,商品状态与本节点交易结果一致。 ### P1-04 商店定价规则:买价、半价回收、卖塔 +10%、耐久折价 @@ -114,7 +118,7 @@ - `Assets/GameMain/Scripts/Entity/` 或库存相关域对象 - 视需要调整 `Assets/GameMain/DataTables/ShopPrice.txt` - 具体拆分: - - 抽出统一价格解析入口:基础买价、出售价、塔出售加成、耐久折价都走同一公式服务。 + - 抽出统一价格解析入口:商店买价、背包组件出售价、背包防御塔出售价、塔出售加成、耐久折价都走同一公式服务。 - 明确“组件价格”和“整塔价格”关系,不能在 UI 里临时把三组件价格相加后再乘一个魔法数。 - 明确耐久折价规则:按当前耐久比例折价,还是按损坏阈值分段折价;先写死规则文档,再落代码。 - 如果 `ShopPrice.txt` 只够表达品质区间,新增字段或新表承载倍率,不要继续在代码里硬编码。