Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
555 lines
14 KiB
C++
555 lines
14 KiB
C++
// GameObjPropPanal.cpp : implementation file
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "MW4GameEd2.h"
|
|
#include "GameObjPropPanal.h"
|
|
#include "EdGameObject.h"
|
|
#include "PanelDlg.h"
|
|
#include "GameInterface.h"
|
|
#include "ObjProps.h"
|
|
#include "ChangeResDlg.h"
|
|
#include "AddScriptDlg.h"
|
|
|
|
#include <Adept\Entity.hpp>
|
|
#include <GameOS\ToolOS.hpp>
|
|
#include <Adept\Resource.hpp>
|
|
|
|
using namespace Adept;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CGameObjPropPanal dialog
|
|
|
|
|
|
CGameObjPropPanal::CGameObjPropPanal( EdGameObject*obj,UndoCommand **cmd,CWnd* pParent /*=NULL*/)
|
|
: CPanelDlg(CGameObjPropPanal::IDD, pParent)
|
|
{
|
|
MyMultiProps=NULL;
|
|
MyObj=obj;
|
|
CmdLst=cmd;
|
|
BadScript=false;
|
|
//{{AFX_DATA_INIT(CGameObjPropPanal)
|
|
m_oMCNum0 = 0.0f;
|
|
m_oMCNum1 = 0.0f;
|
|
m_oMCNum2 = 0.0f;
|
|
m_oMCNum3 = 0.0f;
|
|
m_oMCNum4 = 0.0f;
|
|
m_oMCNum5 = 0.0f;
|
|
m_oMCNum6 = 0.0f;
|
|
m_oMCNum7 = 0.0f;
|
|
m_oMCNum8 = 0.0f;
|
|
m_oMCNum9 = 0.0f;
|
|
m_ResType = _T("");
|
|
//}}AFX_DATA_INIT
|
|
|
|
MemCell[0]=&m_oMemCell0;
|
|
MemCell[1]=&m_oMemCell1;
|
|
MemCell[2]=&m_oMemCell2;
|
|
MemCell[3]=&m_oMemCell3;
|
|
MemCell[4]=&m_oMemCell4;
|
|
MemCell[5]=&m_oMemCell5;
|
|
MemCell[6]=&m_oMemCell6;
|
|
MemCell[7]=&m_oMemCell7;
|
|
MemCell[8]=&m_oMemCell8;
|
|
MemCell[9]=&m_oMemCell9;
|
|
|
|
MCNum[0]=&m_oMCNum0;
|
|
MCNum[1]=&m_oMCNum1;
|
|
MCNum[2]=&m_oMCNum2;
|
|
MCNum[3]=&m_oMCNum3;
|
|
MCNum[4]=&m_oMCNum4;
|
|
MCNum[5]=&m_oMCNum5;
|
|
MCNum[6]=&m_oMCNum6;
|
|
MCNum[7]=&m_oMCNum7;
|
|
MCNum[8]=&m_oMCNum8;
|
|
MCNum[9]=&m_oMCNum9;
|
|
|
|
}
|
|
|
|
CGameObjPropPanal::CGameObjPropPanal(CObjProps *gobj,CWnd* pParent /*=NULL*/)
|
|
: CPanelDlg(CGameObjPropPanal::IDD, pParent)
|
|
{
|
|
MyMultiProps=gobj;
|
|
MyObj=NULL;
|
|
CmdLst=NULL;
|
|
BadScript=false;
|
|
m_oMCNum0 = 0.0f;
|
|
m_oMCNum1 = 0.0f;
|
|
m_oMCNum2 = 0.0f;
|
|
m_oMCNum3 = 0.0f;
|
|
m_oMCNum4 = 0.0f;
|
|
m_oMCNum5 = 0.0f;
|
|
m_oMCNum6 = 0.0f;
|
|
m_oMCNum7 = 0.0f;
|
|
m_oMCNum8 = 0.0f;
|
|
m_oMCNum9 = 0.0f;
|
|
m_ResType = _T("");
|
|
|
|
MemCell[0]=&m_oMemCell0;
|
|
MemCell[1]=&m_oMemCell1;
|
|
MemCell[2]=&m_oMemCell2;
|
|
MemCell[3]=&m_oMemCell3;
|
|
MemCell[4]=&m_oMemCell4;
|
|
MemCell[5]=&m_oMemCell5;
|
|
MemCell[6]=&m_oMemCell6;
|
|
MemCell[7]=&m_oMemCell7;
|
|
MemCell[8]=&m_oMemCell8;
|
|
MemCell[9]=&m_oMemCell9;
|
|
|
|
MCNum[0]=&m_oMCNum0;
|
|
MCNum[1]=&m_oMCNum1;
|
|
MCNum[2]=&m_oMCNum2;
|
|
MCNum[3]=&m_oMCNum3;
|
|
MCNum[4]=&m_oMCNum4;
|
|
MCNum[5]=&m_oMCNum5;
|
|
MCNum[6]=&m_oMCNum6;
|
|
MCNum[7]=&m_oMCNum7;
|
|
MCNum[8]=&m_oMCNum8;
|
|
MCNum[9]=&m_oMCNum9;
|
|
|
|
}
|
|
|
|
|
|
void CGameObjPropPanal::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CPanelDlg::DoDataExchange(pDX);
|
|
//{{AFX_DATA_MAP(CGameObjPropPanal)
|
|
DDX_Control(pDX, IDC_CHANGE, m_ChangeBut);
|
|
DDX_Control(pDX, IDC_SKINLIST, m_SkinListCtrl);
|
|
DDX_Control(pDX, IDC_RESTYPE, m_RTypeCtl);
|
|
DDX_Control(pDX, IDC_SCRIPT, m_ScriptList);
|
|
DDX_Control(pDX, IDC_MEMCELL0, m_oMemCell0);
|
|
DDX_Control(pDX, IDC_MEMCELL1, m_oMemCell1);
|
|
DDX_Control(pDX, IDC_MEMCELL2, m_oMemCell2);
|
|
DDX_Control(pDX, IDC_MEMCELL3, m_oMemCell3);
|
|
DDX_Control(pDX, IDC_MEMCELL4, m_oMemCell4);
|
|
DDX_Control(pDX, IDC_MEMCELL5, m_oMemCell5);
|
|
DDX_Control(pDX, IDC_MEMCELL6, m_oMemCell6);
|
|
DDX_Control(pDX, IDC_MEMCELL7, m_oMemCell7);
|
|
DDX_Control(pDX, IDC_MEMCELL8, m_oMemCell8);
|
|
DDX_Control(pDX, IDC_MEMCELL9, m_oMemCell9);
|
|
DDX_Control(pDX, IDC_ALIGNLIST, m_Align);
|
|
DDX_Control(pDX, IDC_DRIVERTYPE_COMBO, m_DriverType);
|
|
DDX_Text(pDX, IDC_MEMCELL0, m_oMCNum0);
|
|
DDX_Text(pDX, IDC_MEMCELL1, m_oMCNum1);
|
|
DDX_Text(pDX, IDC_MEMCELL2, m_oMCNum2);
|
|
DDX_Text(pDX, IDC_MEMCELL3, m_oMCNum3);
|
|
DDX_Text(pDX, IDC_MEMCELL4, m_oMCNum4);
|
|
DDX_Text(pDX, IDC_MEMCELL5, m_oMCNum5);
|
|
DDX_Text(pDX, IDC_MEMCELL6, m_oMCNum6);
|
|
DDX_Text(pDX, IDC_MEMCELL7, m_oMCNum7);
|
|
DDX_Text(pDX, IDC_MEMCELL8, m_oMCNum8);
|
|
DDX_Text(pDX, IDC_MEMCELL9, m_oMCNum9);
|
|
DDX_Text(pDX, IDC_RESTYPE, m_ResType);
|
|
//}}AFX_DATA_MAP
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CGameObjPropPanal, CPanelDlg)
|
|
//{{AFX_MSG_MAP(CGameObjPropPanal)
|
|
ON_CBN_SELCHANGE(IDC_DRIVERTYPE_COMBO, OnSelchangeDrivertypeCombo)
|
|
ON_BN_CLICKED(IDC_ADDSCRIPT, OnAddscript)
|
|
ON_BN_CLICKED(IDC_RESTYPE, OnRestype)
|
|
ON_CBN_SELCHANGE(IDC_SKINLIST, OnSelchangeSkinlist)
|
|
ON_CBN_SELCHANGE(IDC_SCRIPT, OnSelchangeScript)
|
|
ON_EN_CHANGE(IDC_MEMCELL9, OnChangeMemcell9)
|
|
ON_EN_CHANGE(IDC_MEMCELL8, OnChangeMemcell8)
|
|
ON_EN_CHANGE(IDC_MEMCELL7, OnChangeMemcell7)
|
|
ON_EN_CHANGE(IDC_MEMCELL6, OnChangeMemcell6)
|
|
ON_EN_CHANGE(IDC_MEMCELL5, OnChangeMemcell5)
|
|
ON_EN_CHANGE(IDC_MEMCELL4, OnChangeMemcell4)
|
|
ON_EN_CHANGE(IDC_MEMCELL3, OnChangeMemcell3)
|
|
ON_EN_CHANGE(IDC_MEMCELL2, OnChangeMemcell2)
|
|
ON_EN_CHANGE(IDC_MEMCELL1, OnChangeMemcell1)
|
|
ON_EN_CHANGE(IDC_MEMCELL0, OnChangeMemcell0)
|
|
ON_CBN_SELCHANGE(IDC_ALIGNLIST, OnSelchangeAlignlist)
|
|
ON_BN_CLICKED(IDC_CHANGE, OnChange)
|
|
ON_BN_CLICKED(IDC_INVUL, OnInvul)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CGameObjPropPanal message handlers
|
|
BOOL CGameObjPropPanal::OnInitDialog()
|
|
{
|
|
CPanelDlg::OnInitDialog();
|
|
int i,itm,align;
|
|
|
|
for(i=0;i<Entity::LastAlignment;i++)
|
|
{
|
|
itm=m_Align.AddString(Entity::AlignmentAsciiToText(i));
|
|
m_Align.SetItemData(itm,i);
|
|
}
|
|
|
|
|
|
Verify(!(MyMultiProps && MyObj));
|
|
if(MyMultiProps!=NULL)
|
|
{
|
|
align=MyMultiProps->GetAlignment();
|
|
|
|
m_SkinListCtrl.SetCurSel(m_SkinListCtrl.FindString(-1,GetSkinName(MyMultiProps->GetSkin())));
|
|
MyMultiProps->GetSkin();
|
|
CheckDlgButton(IDC_INVUL,MyMultiProps->GetInvulnerable()?1:0);
|
|
|
|
// m_Script=MyMultiProps->Script;
|
|
// m_Driver=MyMultiProps->Driver;
|
|
//Do driver Code here
|
|
|
|
for (int index = 0;index <MEMCELLS;index++)
|
|
{
|
|
*(MCNum[index])=MyMultiProps->GetMemoryCell(index);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
MyObj->FillSkinList(&m_SkinListCtrl);
|
|
align=MyObj->GetAlignment();
|
|
|
|
CString str;
|
|
m_SkinListCtrl.SetCurSel(m_SkinListCtrl.FindString(-1,MyObj->GetSkinText()));
|
|
CheckDlgButton(IDC_INVUL,MyObj->IsInvulnerable()?1:0);
|
|
m_ChangeBut.ShowWindow(MyObj->CanChangeType()?SW_SHOW:SW_HIDE);
|
|
|
|
|
|
if (MyObj->HasAI() == true)
|
|
{
|
|
for (int index = 0;index <MEMCELLS;index++)
|
|
{
|
|
*(MCNum[index])=MyObj->GetMemoryCell(index);
|
|
}
|
|
}
|
|
|
|
m_ResType=MyObj->GetResourceName();
|
|
|
|
if (MyObj->GetMyEntity() && MyObj->GetMyEntity()->IsDerivedFrom(MWObject::DefaultData))
|
|
{
|
|
GetDlgItem(IDC_INVUL)->EnableWindow(TRUE);
|
|
}
|
|
else
|
|
{
|
|
GetDlgItem(IDC_INVUL)->EnableWindow(FALSE);
|
|
}
|
|
|
|
}
|
|
|
|
FillScriptList();
|
|
FillDriverList();
|
|
SetControlState();
|
|
|
|
for(i=0;i<m_Align.GetCount();i++)
|
|
if(m_Align.GetItemData(i)==align)
|
|
{
|
|
m_Align.SetCurSel(i);
|
|
}
|
|
|
|
|
|
UpdateData(FALSE);
|
|
|
|
for(i=0;i<MEMCELLS;i++) MemCellChange[i]=false;
|
|
|
|
InvChange=RTypeChange=ScriptChange=AlignChange=DriverChange=SkinChange=false;
|
|
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
|
// EXCEPTION: OCX Property Pages should return FALSE
|
|
}
|
|
|
|
|
|
void CGameObjPropPanal::OnOK()
|
|
{
|
|
UpdateData(TRUE);
|
|
CString script_name;
|
|
int align;
|
|
int skinsel;
|
|
skinsel=m_SkinListCtrl.GetCurSel();
|
|
m_ScriptList.GetLBText(m_ScriptList.GetCurSel(),script_name);
|
|
|
|
CString driver_name;
|
|
m_DriverType.GetLBText(m_DriverType.GetCurSel(),driver_name);
|
|
|
|
align=m_Align.GetItemData(m_Align.GetCurSel());
|
|
|
|
Verify(!(MyMultiProps && MyObj));
|
|
if(MyMultiProps!=NULL)
|
|
{
|
|
if(AlignChange) MyMultiProps->SetAlignment(align);
|
|
if(SkinChange && skinsel>=0) MyMultiProps->SetSkin(m_SkinListCtrl.GetItemData(skinsel));
|
|
if(DriverChange) MyMultiProps->SetDriver(driver_name);
|
|
if(ScriptChange) MyMultiProps->SetScript(script_name);
|
|
if(RTypeChange) MyMultiProps->SetSrcResource(ResPath);
|
|
if(InvChange) MyMultiProps->SetInvulnerable(IsDlgButtonChecked(IDC_INVUL)?true:false);
|
|
|
|
|
|
for (int index = 0;index <MEMCELLS;index++)
|
|
{
|
|
if(MemCellChange[index])
|
|
MyMultiProps->SetMemoryCell(index,*(MCNum[index]));
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if(RTypeChange)
|
|
{
|
|
MyObj->ChangeTo(ResPath);
|
|
}
|
|
|
|
/*
|
|
if ((driver_name.GetLength() == 0) ||
|
|
(driver_name.CompareNoCase("<NONE>")))
|
|
{
|
|
driver_name = MyObj->GetBestAIType();
|
|
|
|
int selected_driver(m_DriverType.FindStringExact(-1,driver_name));
|
|
|
|
if (selected_driver != CB_ERR)
|
|
{
|
|
m_DriverType.SetCurSel(selected_driver);
|
|
}
|
|
}
|
|
*/
|
|
|
|
CString driver_text;
|
|
m_DriverType.GetLBText(m_DriverType.GetCurSel(),driver_text);
|
|
|
|
MyObj->SetAIType(driver_text);
|
|
MyObj->SetAlignment(align);
|
|
if( skinsel>=0)
|
|
MyObj->SetSkin((char)m_SkinListCtrl.GetItemData(skinsel));
|
|
MyObj->SetInvulnerable(IsDlgButtonChecked(IDC_INVUL)?true:false);
|
|
|
|
if (!driver_text.CompareNoCase("<NONE>") || !script_name.CompareNoCase("<NONE>") || BadScript)
|
|
{
|
|
MyObj->SetScriptName("");
|
|
}
|
|
else
|
|
{
|
|
Verify(script_name.GetLength() > 0);
|
|
|
|
if (MyObj->HasAI() == false)
|
|
MyObj->SetDefaultAI();
|
|
|
|
CString path;
|
|
path.Format("Content\\Missions\\%s\\Scripts\\%s.abl",MyObj->GetMissionName(),script_name);
|
|
|
|
MyObj->SetScriptName(path);
|
|
}
|
|
|
|
if (MyObj->HasAI() == true)
|
|
{
|
|
for (int index = 0;index<MEMCELLS;index++)
|
|
{
|
|
MyObj->SetMemoryCell(index,*(MCNum[index]));
|
|
}
|
|
}
|
|
|
|
MyObj->RemoveFromMap();
|
|
MyObj->AddToMap();
|
|
}
|
|
// CPanelDlg::OnOK();
|
|
}
|
|
|
|
|
|
|
|
|
|
void CGameObjPropPanal::SetControlState()
|
|
{
|
|
CString text;
|
|
m_DriverType.GetLBText(m_DriverType.GetCurSel(),text);
|
|
bool state;
|
|
if(text.CompareNoCase("<NONE>")) state=true;
|
|
else
|
|
state=false;
|
|
|
|
for (int i=0;i<MEMCELLS;i++)
|
|
{
|
|
MemCell[i]->EnableWindow(state);
|
|
}
|
|
|
|
if(!BadScript)
|
|
m_ScriptList.EnableWindow(state);
|
|
}
|
|
|
|
void CGameObjPropPanal::OnSelchangeDrivertypeCombo()
|
|
{
|
|
SetControlState();
|
|
DriverChange=true;
|
|
}
|
|
|
|
|
|
void CGameObjPropPanal::OnAddscript()
|
|
{
|
|
CAddScriptDlg dlg(this);
|
|
if(dlg.DoModal()==IDOK)
|
|
{
|
|
FillScriptList();
|
|
}
|
|
|
|
}
|
|
|
|
void CGameObjPropPanal::FillScriptList()
|
|
{
|
|
m_ScriptList.ResetContent();
|
|
m_ScriptList.AddString("<NONE>");
|
|
CString mis_name=MyObj?MyObj->GetMissionName():MyMultiProps->MissionName;
|
|
CString script_name;
|
|
|
|
|
|
// GetScripts from REsources
|
|
if(Adept::ResourceManager::Instance==NULL) return;
|
|
Adept::Resource curres;
|
|
curres.First(NULL);
|
|
|
|
|
|
CString res_name,base_name,dir_name;
|
|
while(curres.ReadAndNext())
|
|
{
|
|
res_name=(char *)curres.GetName();
|
|
res_name.MakeLower();
|
|
if(res_name.Find("missions\\"+mis_name+"\\scripts\\")>-1 && res_name.Find(".abl")>-1 )
|
|
{
|
|
script_name=res_name.Mid(res_name.ReverseFind('\\')+1);
|
|
script_name=script_name.Left(script_name.ReverseFind('.'));
|
|
m_ScriptList.AddString(script_name);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// >>>>>>>>>>>>>>>>>>> Get extra files from disk
|
|
WIN32_FIND_DATA file_data;
|
|
HANDLE file_find;
|
|
|
|
CString filter;
|
|
filter.Format("Content\\Missions\\%s\\Scripts\\*.abl",mis_name);
|
|
file_find=FindFirstFile(filter,&file_data);
|
|
|
|
if(file_find != INVALID_HANDLE_VALUE)
|
|
{
|
|
do
|
|
{
|
|
script_name=file_data.cFileName;
|
|
script_name=script_name.Left(script_name.ReverseFind('.'));
|
|
if(m_ScriptList.FindString(-1,script_name)<0)
|
|
m_ScriptList.AddString(script_name);
|
|
}
|
|
while (FindNextFile(file_find,&file_data));
|
|
}
|
|
|
|
|
|
UpdateScriptSelection();
|
|
|
|
}
|
|
|
|
void CGameObjPropPanal::UpdateScriptSelection()
|
|
{
|
|
CString sname=MyObj?MyObj->GetScriptName():MyMultiProps->GetScript();
|
|
|
|
if(sname=="") {m_ScriptList.SetCurSel(0); return;}
|
|
|
|
int idx;
|
|
idx=sname.ReverseFind('.');
|
|
if(idx!=-1)
|
|
sname=sname.Left(idx);
|
|
|
|
idx=sname.ReverseFind('\\');
|
|
if(idx!=-1)
|
|
sname=sname.Mid(idx+1);
|
|
|
|
CString lstr;
|
|
int i;
|
|
i=0;
|
|
m_ScriptList.GetLBText(i,lstr);
|
|
|
|
while(i<m_ScriptList.GetCount() && sname.CompareNoCase(lstr))
|
|
{
|
|
i++;
|
|
m_ScriptList.GetLBText(i,lstr);
|
|
}
|
|
|
|
|
|
Verify(i<m_ScriptList.GetCount());
|
|
|
|
if(i<m_ScriptList.GetCount())
|
|
{
|
|
m_ScriptList.SetCurSel(i);
|
|
}
|
|
else
|
|
{
|
|
BadScript=true;
|
|
|
|
m_ScriptList.ResetContent();
|
|
m_ScriptList.AddString("Error");
|
|
m_ScriptList.SetCurSel(0);
|
|
m_ScriptList.EnableWindow(FALSE);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
void CGameObjPropPanal::OnRestype()
|
|
{
|
|
}
|
|
|
|
|
|
void CGameObjPropPanal::FillDriverList()
|
|
{
|
|
m_DriverType.ResetContent();
|
|
m_DriverType.AddString("<NONE>");
|
|
int selected_driver;
|
|
|
|
if(MyMultiProps!=NULL)
|
|
{
|
|
|
|
selected_driver=m_DriverType.FindStringExact(-1,MyMultiProps->GetDriver());
|
|
}
|
|
else
|
|
{
|
|
MyObj->FillValidAITypes(&m_DriverType);
|
|
selected_driver=m_DriverType.FindStringExact(-1,MyObj->GetAIType());
|
|
|
|
}
|
|
if (selected_driver != CB_ERR)
|
|
m_DriverType.SetCurSel(selected_driver);
|
|
else
|
|
m_DriverType.SetCurSel(0);
|
|
}
|
|
|
|
|
|
void CGameObjPropPanal::OnSelchangeSkinlist() { SkinChange=true;}
|
|
void CGameObjPropPanal::OnSelchangeScript() { ScriptChange=true;}
|
|
void CGameObjPropPanal::OnChangeMemcell9() {MemCellChange[9]=true;}
|
|
void CGameObjPropPanal::OnChangeMemcell8() {MemCellChange[8]=true;}
|
|
void CGameObjPropPanal::OnChangeMemcell7() {MemCellChange[7]=true;}
|
|
void CGameObjPropPanal::OnChangeMemcell6() {MemCellChange[6]=true;}
|
|
void CGameObjPropPanal::OnChangeMemcell5() {MemCellChange[5]=true;}
|
|
void CGameObjPropPanal::OnChangeMemcell4() {MemCellChange[4]=true;}
|
|
void CGameObjPropPanal::OnChangeMemcell3() {MemCellChange[3]=true;}
|
|
void CGameObjPropPanal::OnChangeMemcell2() {MemCellChange[2]=true;}
|
|
void CGameObjPropPanal::OnChangeMemcell1() {MemCellChange[1]=true;}
|
|
void CGameObjPropPanal::OnChangeMemcell0() {MemCellChange[0]=true;}
|
|
void CGameObjPropPanal::OnSelchangeAlignlist() {AlignChange=true;}
|
|
void CGameObjPropPanal::OnInvul() { InvChange=true; }
|
|
|
|
void CGameObjPropPanal::OnChange()
|
|
{
|
|
CChangeResDlg dlg(this);
|
|
|
|
if(dlg.DoModal()==IDOK)
|
|
{
|
|
ResPath=m_ResType=dlg.m_NewResource;
|
|
m_ResType=m_ResType.Left(m_ResType.ReverseFind('.'));
|
|
m_ResType=m_ResType.Mid(m_ResType.ReverseFind('\\')+1);
|
|
RTypeChange=true;
|
|
UpdateData(FALSE);
|
|
}
|
|
|
|
}
|
|
|