620 lines
21 KiB
C#
620 lines
21 KiB
C#
using BrewMonster.Network;
|
|
using BrewMonster.Scripts.Managers;
|
|
using BrewMonster.Scripts.Task;
|
|
using BrewMonster.UI;
|
|
using CSNetwork;
|
|
using ModelRenderer.Scripts.Common;
|
|
using PerfectWorld.Scripts.Managers;
|
|
using PerfectWorld.Scripts.Task;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using BrewMonster.Scripts;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.UI;
|
|
|
|
namespace BrewMonster
|
|
{
|
|
public class DlgInstall : AUIDialog
|
|
{
|
|
public enum InstallMode
|
|
{
|
|
Enchase,
|
|
Disenchase
|
|
}
|
|
|
|
[Header("Mode")]
|
|
[SerializeField] private InstallMode m_Mode = InstallMode.Enchase;
|
|
|
|
[Header("Slot First")]
|
|
[SerializeField] private Transform m_SlotFirstParent;
|
|
[SerializeField] private TextMeshProUGUI m_TxtFirstName;
|
|
|
|
[Header("Slot Second")]
|
|
[SerializeField] private Transform m_SlotSecondlParent;
|
|
[SerializeField] private TextMeshProUGUI m_TxtSecondName;
|
|
|
|
[Header("Buttons and Money")]
|
|
[SerializeField] private TextMeshProUGUI m_TxtMoney;
|
|
[SerializeField] private Button m_BtnMergeOrReset;
|
|
[SerializeField] private Button m_BtnCancel;
|
|
|
|
[SerializeField] private Sprite khung_item;
|
|
[SerializeField] private Transform itemInventoryRoot;
|
|
|
|
private EC_IvtrItem m_SelectedEquip;
|
|
private EC_IvtrItem m_SelectedMaterial;
|
|
|
|
private int m_FirstInvSlot = -1;
|
|
private int m_SecondInvSlot = -1;
|
|
private int m_install_price = -1;
|
|
|
|
public override void Awake()
|
|
{
|
|
base.Awake();
|
|
RegisterDrop(m_SlotFirstParent, OnDropEquip);
|
|
RegisterClick(m_SlotFirstParent, OnClickEquipSlot);
|
|
|
|
if (m_Mode == InstallMode.Enchase && m_SlotSecondlParent != null)
|
|
{
|
|
RegisterDrop(m_SlotSecondlParent, OnDropMaterial);
|
|
RegisterClick(m_SlotSecondlParent, OnClickMaterialSlot);
|
|
}
|
|
}
|
|
|
|
public override void Update()
|
|
{
|
|
#if UNITY_EDITOR || UNITY_STANDALONE
|
|
if (Input.GetMouseButtonDown(0))
|
|
{
|
|
CheckHidePanel(Input.mousePosition);
|
|
}
|
|
#else
|
|
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
|
|
{
|
|
CheckHidePanel(Input.GetTouch(0).position);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
public override void OnEnable()
|
|
{
|
|
base.OnEnable();
|
|
//todo need to set from other class
|
|
// SetName("Win_Enchase");
|
|
m_BtnMergeOrReset.onClick.AddListener(OnClickedMergeOrReset);
|
|
m_BtnCancel.onClick.AddListener(OnClickedCancel);
|
|
m_install_price = -1;
|
|
if(m_SlotSecondlParent != null)
|
|
m_SlotSecondlParent.gameObject.SetActive(m_Mode == InstallMode.Enchase);
|
|
}
|
|
|
|
public override void OnDisable()
|
|
{
|
|
base.OnDisable();
|
|
m_BtnMergeOrReset.onClick.RemoveListener(OnClickedMergeOrReset);
|
|
m_BtnCancel.onClick.RemoveListener(OnClickedCancel);
|
|
}
|
|
|
|
public void CloseInstall()
|
|
{
|
|
gameObject.SetActive(false);
|
|
RestoreInventoryColors();
|
|
}
|
|
|
|
private void RestoreInventoryColors()
|
|
{
|
|
var canvas = FindFirstObjectByType<Canvas>();
|
|
if (canvas == null)
|
|
return;
|
|
|
|
if (m_FirstInvSlot >= 0)
|
|
{
|
|
var btn = FindInventoryButtonBySlot(m_FirstInvSlot);
|
|
SetInventorySlotGray(btn, false);
|
|
m_FirstInvSlot = -1;
|
|
}
|
|
|
|
if (m_SecondInvSlot >= 0)
|
|
{
|
|
var btn = FindInventoryButtonBySlot(m_SecondInvSlot);
|
|
SetInventorySlotGray(btn, false);
|
|
m_SecondInvSlot = -1;
|
|
}
|
|
}
|
|
|
|
private Button FindInventoryButtonBySlot(int slot)
|
|
{
|
|
var inventoryUI = FindFirstObjectByType<EC_InventoryUI>();
|
|
if (inventoryUI == null)
|
|
return null;
|
|
|
|
var field = typeof(EC_InventoryUI)
|
|
.GetField("inventoryPackButtons", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|
|
|
if (field == null)
|
|
return null;
|
|
|
|
var list = field.GetValue(inventoryUI) as List<Button>;
|
|
if (list == null || slot < 0 || slot >= list.Count)
|
|
return null;
|
|
|
|
return list[slot];
|
|
}
|
|
|
|
private void RegisterDrop(Transform target, Action<PointerEventData> callback)
|
|
{
|
|
var trigger = target.GetComponent<EventTrigger>();
|
|
if (trigger == null)
|
|
trigger = target.gameObject.AddComponent<EventTrigger>();
|
|
|
|
trigger.triggers.Clear();
|
|
|
|
var entry = new EventTrigger.Entry
|
|
{
|
|
eventID = EventTriggerType.Drop
|
|
};
|
|
entry.callback.AddListener((data) =>
|
|
{
|
|
callback((PointerEventData)data);
|
|
});
|
|
|
|
trigger.triggers.Add(entry);
|
|
}
|
|
|
|
private void RegisterClick(Transform target, Action<PointerEventData> callback)
|
|
{
|
|
if(target == null) return;
|
|
|
|
var trigger = target.GetComponent<EventTrigger>();
|
|
if (trigger == null)
|
|
trigger = target.gameObject.AddComponent<EventTrigger>();
|
|
|
|
var entry = new EventTrigger.Entry
|
|
{
|
|
eventID = EventTriggerType.PointerClick
|
|
};
|
|
entry.callback.AddListener((data) =>
|
|
{
|
|
callback((PointerEventData)data);
|
|
});
|
|
|
|
trigger.triggers.Add(entry);
|
|
}
|
|
|
|
private void OnClickEquipSlot(PointerEventData eventData)
|
|
{
|
|
if (m_SelectedEquip != null)
|
|
{
|
|
ReturnItemToInventory(m_FirstInvSlot);
|
|
ClearEquipSlot();
|
|
}
|
|
}
|
|
|
|
private void OnClickMaterialSlot(PointerEventData eventData)
|
|
{
|
|
if (m_SelectedMaterial != null)
|
|
{
|
|
ReturnItemToInventory(m_SecondInvSlot);
|
|
ClearMaterialSlot();
|
|
}
|
|
}
|
|
|
|
private EC_IvtrItem GetItemFromDrag(PointerEventData eventData)
|
|
{
|
|
if (eventData.pointerDrag == null)
|
|
return null;
|
|
|
|
var btn = eventData.pointerDrag.GetComponent<Button>();
|
|
if (btn == null)
|
|
return null;
|
|
|
|
// Slot index
|
|
int slotIndex = btn.transform.GetSiblingIndex();
|
|
|
|
// Inventory package = 0
|
|
var host = CECGameRun.Instance?.GetHostPlayer();
|
|
if (host == null)
|
|
return null;
|
|
|
|
var inv = host.GetInventory(0);
|
|
if (inv == null)
|
|
return null;
|
|
|
|
return inv.GetItem(slotIndex, false);
|
|
}
|
|
|
|
private void OnDropEquip(PointerEventData eventData)
|
|
{
|
|
if (eventData.pointerDrag == null)
|
|
return;
|
|
|
|
var btn = eventData.pointerDrag.GetComponent<Button>();
|
|
if (btn == null)
|
|
return;
|
|
|
|
int slotIndex = btn.transform.GetSiblingIndex();
|
|
|
|
var item = GetItemFromDrag(eventData);
|
|
if (item == null)
|
|
return;
|
|
|
|
EC_IvtrItem detailedItem = EC_IvtrItem.CreateItem(item.m_tid, item.m_expire_date, item.m_iCount);
|
|
if (item.Content != null && item.Content.Length > 0)
|
|
detailedItem.SetItemInfo(item.Content, item.Content.Length);
|
|
else
|
|
detailedItem.GetDetailDataFromLocal();
|
|
|
|
if (m_FirstInvSlot >= 0)
|
|
{
|
|
var previosBtn = FindInventoryButtonBySlot(m_FirstInvSlot);
|
|
SetInventorySlotGray(previosBtn, false);
|
|
}
|
|
|
|
m_SelectedEquip?.Freeze(false);
|
|
m_SelectedEquip = detailedItem;
|
|
m_FirstInvSlot = slotIndex;
|
|
|
|
m_TxtFirstName.text = detailedItem.GetName();
|
|
SetSlotIcon(m_SlotFirstParent, detailedItem);
|
|
|
|
SetInventorySlotGray(btn, true);
|
|
|
|
detailedItem.Freeze(true);
|
|
|
|
if(m_Mode == InstallMode.Disenchase)
|
|
{
|
|
CalculateUninstallPrice(detailedItem);
|
|
}
|
|
}
|
|
|
|
private void OnDropMaterial(PointerEventData eventData)
|
|
{
|
|
if (eventData.pointerDrag == null)
|
|
return;
|
|
|
|
var btn = eventData.pointerDrag.GetComponent<Button>();
|
|
if (btn == null)
|
|
return;
|
|
|
|
int slotIndex = btn.transform.GetSiblingIndex();
|
|
|
|
var item = GetItemFromDrag(eventData);
|
|
if (item == null)
|
|
return;
|
|
|
|
EC_IvtrItem detailedItem = EC_IvtrItem.CreateItem(item.m_tid, item.m_expire_date, item.m_iCount);
|
|
if (item.Content != null && item.Content.Length > 0)
|
|
detailedItem.SetItemInfo(item.Content, item.Content.Length);
|
|
else
|
|
detailedItem.GetDetailDataFromLocal();
|
|
|
|
if (m_SecondInvSlot >= 0)
|
|
{
|
|
var previosBtn = FindInventoryButtonBySlot(m_SecondInvSlot);
|
|
SetInventorySlotGray(previosBtn, false);
|
|
}
|
|
|
|
m_SelectedMaterial?.Freeze(false);
|
|
m_SelectedMaterial = detailedItem;
|
|
m_SelectedMaterial?.Freeze(true);
|
|
m_SecondInvSlot = slotIndex;
|
|
|
|
m_TxtSecondName.text = detailedItem.GetName();
|
|
SetSlotIcon(m_SlotSecondlParent, detailedItem);
|
|
|
|
SetInventorySlotGray(btn, true);
|
|
|
|
// GetGameUIMan().PlayItemSound(pIvtrSrc, true);
|
|
EC_IvtrStone pStone = null;
|
|
STONE_ESSENCE pEssence;
|
|
// ACHAR szText[40] = _AL("0");
|
|
|
|
//check if material is stone and show price to merge
|
|
if(m_SelectedMaterial.GetClassID() == (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_STONE)
|
|
{
|
|
pStone = (EC_IvtrStone)m_SelectedMaterial;
|
|
pEssence = pStone.GetDBEssence();
|
|
m_TxtMoney.text = $"{pEssence.install_price}";
|
|
// a_sprintf(szText, _AL("%d"), pEssence->install_price);
|
|
// m_pTxtGold->SetText(szText);
|
|
}
|
|
// else if( 0 == stricmp(this->GetName(), "Win_Disenchase")
|
|
// && pIvtrSrc->IsEquipment() )
|
|
// {
|
|
// a_LogOutput(1, "[Dat Embed] OnItemDragDrop stricmp(this->GetName(), Win_Disenchase");
|
|
// int i, nAmount = 0, idItem;
|
|
// CECIvtrEquip *pEquip = (CECIvtrEquip *)pIvtrSrc;
|
|
//
|
|
// for( i = 0; i < pEquip->GetHoleNum(); i++ )
|
|
// {
|
|
// idItem = pEquip->GetHoleItem(i);
|
|
// if( idItem <= 0 ) continue;
|
|
//
|
|
// pStone = (CECIvtrStone *)CECIvtrItem::CreateItem(idItem, 0, 1);
|
|
// pEssence = (STONE_ESSENCE *)pStone->GetDBEssence();
|
|
// nAmount += pEssence->uninstall_price;
|
|
// delete pStone;
|
|
// }
|
|
//
|
|
// a_sprintf(szText, _AL("%d"), nAmount);
|
|
// m_pTxtGold->SetText(szText);
|
|
// }
|
|
}
|
|
|
|
private void CalculateUninstallPrice(EC_IvtrItem equipment)
|
|
{
|
|
if (equipment == null || !equipment.IsEquipment())
|
|
return;
|
|
|
|
EC_IvtrEquip pEquip = (EC_IvtrEquip)equipment;
|
|
|
|
int nAmount = 0;
|
|
for (int i = 0; i < pEquip.GetHoleNum(); i++)
|
|
{
|
|
int idIten = pEquip.GetHoleItem(i);
|
|
if (idIten <= 0)
|
|
continue;
|
|
|
|
EC_IvtrStone pStone = EC_IvtrItem.CreateItem(idIten, 0, 1) as EC_IvtrStone;
|
|
if (pStone != null)
|
|
{
|
|
STONE_ESSENCE pEssence = pStone.GetDBEssence();
|
|
nAmount += pEssence.uninstall_price;
|
|
}
|
|
}
|
|
|
|
m_install_price = nAmount;
|
|
m_TxtMoney.text = nAmount.ToString();
|
|
}
|
|
|
|
private void SetInventorySlotGray(Button btn, bool gray)
|
|
{
|
|
if (btn == null)
|
|
return;
|
|
|
|
var img = btn.GetComponent<Image>();
|
|
if (img == null)
|
|
return;
|
|
|
|
img.color = gray
|
|
? new Color(0.3f, 0.3f, 0.3f, 1f)
|
|
: Color.white;
|
|
}
|
|
|
|
private void SetSlotIcon(Transform slot, EC_IvtrItem item)
|
|
{
|
|
if (slot == null || item == null)
|
|
return;
|
|
|
|
var img = slot.GetComponentInChildren<Image>(true);
|
|
if (img == null)
|
|
return;
|
|
|
|
img.sprite = EC_IvtrItemUtils.Instance.ResolveItemIconSprite(item.m_tid);
|
|
img.enabled = img.sprite != null;
|
|
|
|
img.color = Color.white;
|
|
img.SetNativeSize();
|
|
}
|
|
|
|
private void ReturnItemToInventory(int slotIndex)
|
|
{
|
|
if (slotIndex < 0)
|
|
return;
|
|
|
|
var btn = FindInventoryButtonBySlot(slotIndex);
|
|
SetInventorySlotGray(btn, false);
|
|
}
|
|
|
|
private void ClearEquipSlot()
|
|
{
|
|
if (m_FirstInvSlot >= 0)
|
|
{
|
|
ReturnItemToInventory(m_FirstInvSlot);
|
|
}
|
|
|
|
m_SelectedEquip?.Freeze(false);
|
|
m_SelectedEquip = null;
|
|
m_FirstInvSlot = -1;
|
|
m_TxtFirstName.text = "___";
|
|
ClearSlotIcon(m_SlotFirstParent);
|
|
if (m_Mode == InstallMode.Disenchase)
|
|
{
|
|
m_install_price = -1;
|
|
m_TxtMoney.text = "0";
|
|
}
|
|
}
|
|
|
|
private void ClearMaterialSlot()
|
|
{
|
|
if (m_SecondInvSlot >= 0)
|
|
{
|
|
ReturnItemToInventory(m_SecondInvSlot);
|
|
}
|
|
|
|
m_SelectedMaterial?.Freeze(false);
|
|
m_SelectedMaterial = null;
|
|
m_SecondInvSlot = -1;
|
|
m_TxtSecondName.text = "___";
|
|
ClearSlotIcon(m_SlotSecondlParent);
|
|
m_TxtMoney.text = "0";
|
|
m_install_price = -1;
|
|
}
|
|
|
|
private void ClearSlotIcon(Transform slot)
|
|
{
|
|
if (slot == null)
|
|
return;
|
|
|
|
var img = slot.GetComponentInChildren<Image>(true);
|
|
if (img == null)
|
|
return;
|
|
|
|
img.sprite = khung_item;
|
|
}
|
|
private void OnClickedMergeOrReset()
|
|
{
|
|
// PAUIDIALOG pMsgBox;
|
|
CECHostPlayer pHost = GetHostPlayer();
|
|
|
|
if (pHost != null && m_Mode == InstallMode.Enchase)
|
|
{
|
|
// if( !m_pItema->GetDataPtr("ptr_CECIvtrItem") ) return;
|
|
string message = "";
|
|
|
|
int nMoney = m_install_price;
|
|
if (nMoney > pHost.GetMoneyAmount())
|
|
{
|
|
message = GetGameUIMan().GetStringFromTable(226);
|
|
CECUIManager.Instance.ShowMessageBox("", message);
|
|
// GetGameUIMan().GetDialog("")
|
|
// A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
|
|
// pMsgBox->SetLife(3);
|
|
return;
|
|
}
|
|
|
|
EC_IvtrItem pIvtrA = m_SelectedEquip;
|
|
if (!pIvtrA.IsEquipment())
|
|
{
|
|
message = GetGameUIMan().GetStringFromTable(223);
|
|
CECUIManager.Instance.ShowMessageBox("", message);
|
|
// GetGameUIMan().MessageBox("", GetGameUIMan().GetStringFromTable(223), MB_OK,
|
|
// A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
|
|
// pMsgBox.SetLife(3);
|
|
return;
|
|
}
|
|
|
|
EC_IvtrEquip pEquipA = (EC_IvtrEquip)pIvtrA;
|
|
// a_LogOutput(1, "[Dat Embed] Send protocol here");
|
|
// if( 0 == string.CompareOrdinal(GetName(), "Win_Enchase") )
|
|
// {
|
|
if (pEquipA.GetEmptyHoleNum() <= 0)
|
|
{
|
|
message = GetGameUIMan().GetStringFromTable(224);
|
|
CECUIManager.Instance.ShowMessageBox("", message);
|
|
// GetGameUIMan().MessageBox("", GetGameUIMan()->GetStringFromTable(224), MB_OK,
|
|
// A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
|
|
// pMsgBox.SetLife(3);
|
|
return;
|
|
}
|
|
|
|
EC_IvtrItem pIvtrB = m_SelectedMaterial;
|
|
if (pIvtrB == null || !pIvtrB.IsEmbeddable())
|
|
{
|
|
message = GetGameUIMan().GetStringFromTable(225);
|
|
CECUIManager.Instance.ShowMessageBox("", message);
|
|
// GetGameUIMan().MessageBox("", GetGameUIMan().GetStringFromTable(225), MB_OK,
|
|
// A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
|
|
// pMsgBox.SetLife(3);
|
|
return;
|
|
}
|
|
|
|
if (pIvtrB.GetClassID() != (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_STONE)
|
|
return;
|
|
|
|
int nStoneLevel = ((EC_IvtrStone)pIvtrB).GetDBEssence().level;
|
|
int nEquipLevel = -1;
|
|
switch (pEquipA.GetClassID())
|
|
{
|
|
case (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_WEAPON:
|
|
nEquipLevel = ((CECIvtrWeapon)pEquipA).GetDBEssence().level;
|
|
break;
|
|
case (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_ARMOR:
|
|
nEquipLevel = ((EC_IvtrArmor)pEquipA).GetDBEssence().level;
|
|
break;
|
|
case (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_DECORATION:
|
|
nEquipLevel = ((EC_IvtrDecoration)pEquipA).GetDBEssence().level;
|
|
break;
|
|
}
|
|
|
|
if (nStoneLevel > nEquipLevel)
|
|
{
|
|
message = GetGameUIMan().GetStringFromTable(300);
|
|
CECUIManager.Instance.ShowMessageBox("", message);
|
|
// GetGameUIMan().MessageBox("", GetGameUIMan().GetStringFromTable(300), MB_OK,
|
|
// A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
|
|
// pMsgBox.SetLife(3);
|
|
return;
|
|
}
|
|
|
|
//pr
|
|
UnityGameSession.c2s_CmdNPCSevEmbed(
|
|
(ushort)m_SecondInvSlot, (ushort)m_FirstInvSlot,
|
|
pIvtrB.GetTemplateID(), pIvtrA.GetTemplateID());
|
|
ClearEquipSlot();
|
|
ClearMaterialSlot();
|
|
pHost.GetPack(InventoryConst.IVTRTYPE_PACK).UnfreezeAllItems();
|
|
|
|
message = GetGameUIMan().GetStringFromTable(228);
|
|
CECUIManager.Instance.ShowMessageBox("", message);
|
|
|
|
// GetGameUIMan().MessageBox("", GetGameUIMan().GetStringFromTable(228),
|
|
// MB_OK, A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
|
|
// pMsgBox.SetLife(3);
|
|
// }
|
|
// else if( 0 == stricmp(GetName(), "Win_Disenchase") )
|
|
// {
|
|
// a_LogOutput(1, "[Dat Embed] Win_Disenchase");
|
|
// if( pEquipA->GetEmptyHoleNum() == pEquipA->GetHoleNum() )
|
|
// {
|
|
// GetGameUIMan()->MessageBox("", GetGameUIMan()->GetStringFromTable(227), MB_OK,
|
|
// A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
|
|
// pMsgBox->SetLife(3);
|
|
// return;
|
|
// }
|
|
|
|
// GetGameUIMan()->MessageBox("Game_Disenchase", GetGameUIMan()->GetStringFromTable(229),
|
|
// MB_OKCANCEL, A3DCOLORRGBA(255, 255, 255, 160));
|
|
// }
|
|
}
|
|
|
|
else if (pHost != null || m_Mode == InstallMode.Disenchase)
|
|
{
|
|
// TODO: implement uninstall logicq
|
|
}
|
|
}
|
|
|
|
private void OnClickedCancel()
|
|
{
|
|
Show(false);
|
|
}
|
|
|
|
public void ResetInstallUI()
|
|
{
|
|
RestoreInventoryColors();
|
|
|
|
m_SelectedEquip = null;
|
|
if (m_Mode == InstallMode.Enchase)
|
|
{
|
|
m_SelectedMaterial = null;
|
|
m_SecondInvSlot = -1;
|
|
m_TxtSecondName.text = "___";
|
|
ClearSlotIcon(m_SlotSecondlParent);
|
|
}
|
|
|
|
m_FirstInvSlot = -1;
|
|
|
|
m_TxtFirstName.text = "___";
|
|
m_TxtMoney.text = "0";
|
|
m_install_price = -1;
|
|
|
|
ClearSlotIcon(m_SlotFirstParent);
|
|
|
|
gameObject.SetActive(false);
|
|
}
|
|
|
|
private void CheckHidePanel(Vector2 screenPos)
|
|
{
|
|
if (!RectTransformUtility.RectangleContainsScreenPoint(
|
|
itemInventoryRoot as RectTransform, screenPos,
|
|
Camera.main))
|
|
{
|
|
if(itemInventoryRoot!=null)
|
|
itemInventoryRoot.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
}
|
|
} |