485 lines
14 KiB
C++
485 lines
14 KiB
C++
#include "StructItem.h"
|
|
#include "StructInventory.h"
|
|
#include "LogClient/LogClient.h"
|
|
#include "DB_Commands.h"
|
|
|
|
#include <toolkit/XSTLUtil.h>
|
|
#include <toolkit/XConsole.h>
|
|
|
|
StructInventory::StructInventory( InventoryEventReceiver * pEventReceiver ) : m_fWeight( 0.0f ), m_fWeightModifier( 0 ), m_pEventReceiver( pEventReceiver )
|
|
{
|
|
m_vList.reserve( 50 );
|
|
}
|
|
|
|
StructInventory::~StructInventory()
|
|
{
|
|
}
|
|
|
|
struct StructItem* StructInventory::Join( struct StructItem* pItem, const __int64 & cnt, bool bSkipUpdateItemToDB )
|
|
{
|
|
if( cnt <= 0 )
|
|
return NULL;
|
|
|
|
// 중첩 가능한 아이템을 찾았다면
|
|
StructItem *pExistItem = FindJoinablePair( pItem );
|
|
if( pExistItem != NULL )
|
|
{
|
|
// 무게 조정(장착된 아이템의 경우 무게 작용 안 함... -_-; 장착된 아템을 획득 가능한가;;)
|
|
m_fWeight += ( cnt * pExistItem->GetItemBase().fWeight );
|
|
if( pExistItem->GetWearInfo() != -1 )
|
|
m_fWeightModifier -= ( cnt * pExistItem->GetItemBase().fWeight );
|
|
|
|
// 아이템 갯수 늘린다
|
|
setCount( pExistItem, pExistItem->GetCount() + cnt, bSkipUpdateItemToDB );
|
|
}
|
|
|
|
return pExistItem;
|
|
}
|
|
|
|
StructItem* StructInventory::Push( StructItem *pItem, const __int64 & cnt, bool bSkipUpdateItemToDB )
|
|
{
|
|
if( cnt <= 0 )
|
|
{
|
|
_cprint( "StructInventory::Push failed - cnt %I64d for Item code[%d], Count[%I64d], UID[%I64d]\n",
|
|
cnt, pItem->GetItemCode(), pItem->GetCount(), pItem->GetItemUID() );
|
|
FILELOG( "StructInventory::Push failed - cnt %I64d for Item code[%d], Count[%I64d], UID[%I64d]",
|
|
cnt, pItem->GetItemCode(), pItem->GetCount(), pItem->GetItemUID() );
|
|
return NULL;
|
|
}
|
|
|
|
StructItem *pExistItem = Join( pItem, cnt, bSkipUpdateItemToDB );
|
|
|
|
// 합쳐야 하는 경우가 아니라면...
|
|
if( pExistItem == NULL )
|
|
{
|
|
// 무게 조정(장착된 아이템의 경우 무게 작용 안 함... -_-; 장착된 아템을 획득 가능한가;; <- 로그인시 아이템 정보 읽으면서 가능;;)
|
|
m_fWeight += pItem->GetWeight();
|
|
if( pItem->GetWearInfo() != -1 )
|
|
m_fWeightModifier -= pItem->GetWeight();
|
|
|
|
push( pItem, bSkipUpdateItemToDB );
|
|
|
|
return pItem;
|
|
}
|
|
|
|
return pExistItem;
|
|
}
|
|
|
|
StructItem *StructInventory::Pop( StructItem *pItem, const __int64 & cnt, bool bSkipUpdateItemToDB )
|
|
{
|
|
// 유효성 검사
|
|
if( !check( pItem ) )
|
|
return NULL;
|
|
|
|
// 일부만 꺼낼 경우는 아이템을 복제한다
|
|
if( pItem->GetCount() > cnt && cnt > 0 )
|
|
{
|
|
// 일단 모든 속성 복제
|
|
StructItem *pNewItem = StructItem::AllocItem( 0, pItem->GetItemCode(), 1, ItemInstance::BY_DIVIDE );
|
|
pNewItem->CopyFrom( pItem );
|
|
|
|
// 아이템갯수 설정
|
|
pNewItem->SetCount( cnt );
|
|
|
|
// 꺼낸만큼 무게를 줄인다(장착된 아이템은 무게에 포함되지 않았으므로 안 뺌)
|
|
m_fWeight -= ( cnt * pItem->GetItemBase().fWeight );
|
|
if( pItem->GetWearInfo() != -1 )
|
|
m_fWeightModifier += ( cnt * pItem->GetItemBase().fWeight );
|
|
|
|
// 기존 아이템 갯수를 줄인다
|
|
setCount( pItem, pItem->GetCount() - cnt, bSkipUpdateItemToDB );
|
|
|
|
return pNewItem;
|
|
}
|
|
|
|
// 전부다 꺼내는 경우는 컨테이너에서 제거한다.
|
|
if( pItem->GetCount() == cnt )
|
|
{
|
|
// 꺼낸만큼 무게를 줄인다(장착된 아이템은 무게에 포함되지 않았으므로 안 뺌)
|
|
m_fWeight -= ( cnt * pItem->GetItemBase().fWeight );
|
|
if( pItem->GetWearInfo() != -1 )
|
|
m_fWeightModifier += pItem->GetWeight();
|
|
|
|
pop( pItem, bSkipUpdateItemToDB );
|
|
|
|
return pItem;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool StructInventory::check( struct StructItem* pItem ) const
|
|
{
|
|
if( pItem->GetIdx() >= static_cast< int >( m_vList.size() ) )
|
|
{
|
|
_cprint( "wrong indexed item in inventory detected while checking. Item(%I64d)\n", pItem->GetItemUID() );
|
|
FILELOG( "wrong indexed item in inventory detected while chekcing. Item(%I64d)", pItem->GetItemUID() );
|
|
assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
if( m_vList[ pItem->GetIdx() ] != pItem )
|
|
{
|
|
_cprint( "wrong indexed item in inventory detected while checking. Item(%I64d)\n", pItem->GetItemUID() );
|
|
FILELOG( "wrong indexed item in inventory detected while checking. Item(%I64d)", pItem->GetItemUID() );
|
|
assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool StructInventory::Erase( struct StructItem* pItem, const __int64 & cnt, bool bSkipUpdateItemToDB )
|
|
{
|
|
// 유효성 검사
|
|
if( !check( pItem ) ) return false;
|
|
|
|
if( cnt < 1 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// 일부만 지울 경우는 수량만 조절한다.
|
|
if( pItem->GetCount() > cnt )
|
|
{
|
|
// 삭제된만큼 무게를 줄인다(장착된 아이템은 무게에 포함되지 않았으므로 안 뺌)
|
|
m_fWeight -= cnt * pItem->GetItemBase().fWeight;
|
|
if( pItem->GetWearInfo() != -1 )
|
|
m_fWeightModifier += ( cnt * pItem->GetItemBase().fWeight );
|
|
|
|
setCount( pItem, pItem->GetCount() - cnt, bSkipUpdateItemToDB );
|
|
|
|
return true;
|
|
}
|
|
|
|
// 전부다 지우는 경우는 컨테이너에서 제거하고 아이템은 삭제한다.
|
|
m_fWeight -= pItem->GetWeight();
|
|
if( pItem->GetWearInfo() != -1 )
|
|
m_fWeightModifier += pItem->GetWeight();
|
|
|
|
pop( pItem, bSkipUpdateItemToDB );
|
|
|
|
// _oprint( "F:" );
|
|
StructItem::PendFreeItem_( pItem, NULL, 0 );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool StructInventory::Swap( int idx1, int idx2 )
|
|
{
|
|
// 유효성 검사
|
|
if( idx1 == idx2 )
|
|
return false;
|
|
int maxIdx = static_cast< int >( m_vList.size() ) - 1;
|
|
if( idx1 > maxIdx|| idx2 > maxIdx )
|
|
return false;
|
|
if( m_vList[ idx1 ]->GetIdx() != idx1 )
|
|
return false;
|
|
if( m_vList[ idx2 ]->GetIdx() != idx2 )
|
|
return false;
|
|
|
|
// 순서까지 고려하면 햇갈리니까 idx1가 항상 작거나 같게
|
|
if( idx1 > idx2 )
|
|
{
|
|
int temp = idx1;
|
|
idx1 = idx2;
|
|
idx2 = temp;
|
|
}
|
|
|
|
// 우선 리스트 내에서 바꿔주고 인덱스 설정한 후
|
|
StructItem * pTempItem = m_vList[ idx1 ];
|
|
m_vList[ idx1 ] = m_vList[ idx2 ];
|
|
m_vList[ idx2 ] = pTempItem;
|
|
|
|
m_vList[ idx1 ]->SetIdx( idx1 );
|
|
m_vList[ idx2 ]->SetIdx( idx2 );
|
|
|
|
// 마지막으로 PreviousUID까지 바꿔줌 (만약 첫번째 요소와 스왑할 경우 previousUID는 0)
|
|
if( idx1 != 0 )
|
|
m_vList[ idx1 ]->SetPreviousUID( m_vList[ idx1 - 1 ]->GetItemUID() );
|
|
else
|
|
m_vList[ idx1 ]->SetPreviousUID( 0 );
|
|
m_vList[ idx1 ]->TurnOnUpdateFlag();
|
|
m_vList[ idx1+1 ]->SetPreviousUID( m_vList[ idx1 ]->GetItemUID() );
|
|
m_vList[ idx1+1 ]->TurnOnUpdateFlag();
|
|
m_vList[ idx2 ]->SetPreviousUID( m_vList[ idx2 - 1 ]->GetItemUID() );
|
|
m_vList[ idx2 ]->TurnOnUpdateFlag();
|
|
if( idx2 != static_cast< int >( m_vList.size() - 1 ) )
|
|
{
|
|
m_vList[ idx2+1 ]->SetPreviousUID( m_vList[ idx2 ]->GetItemUID() );
|
|
m_vList[ idx2+1 ]->TurnOnUpdateFlag();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
StructItem* StructInventory::Find( ItemUID uid ) const
|
|
{
|
|
std::vector< StructItem* >::const_iterator it;
|
|
for( it = m_vList.begin(); it != m_vList.end(); ++it )
|
|
{
|
|
if( (*it)->GetItemUID() == uid ) return *it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
StructItem* StructInventory::Find( ItemBase::ItemCode code ) const
|
|
{
|
|
std::vector< StructItem* >::const_iterator it;
|
|
for( it = m_vList.begin(); it != m_vList.end(); ++it )
|
|
{
|
|
if( (*it)->GetItemCode() == code ) return *it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
StructItem* StructInventory::Find( ItemBase::ItemCode code, unsigned int flag, bool bFlag ) const
|
|
{
|
|
std::vector< StructItem* >::const_iterator it;
|
|
for( it = m_vList.begin(); it != m_vList.end(); ++it )
|
|
{
|
|
bool bIsOn = (*it)->GetInstanceFlag().IsOn( flag );
|
|
if( (*it)->GetItemCode() == code && ( (bFlag && bIsOn) || (!bFlag && !bIsOn) ) ) return *it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void StructInventory::Find( ItemBase::ItemCode code, std::vector< StructItem * > & vList )
|
|
{
|
|
std::vector< StructItem* >::const_iterator it;
|
|
for( it = m_vList.begin(); it != m_vList.end(); ++it )
|
|
{
|
|
if( (*it)->GetItemCode() == code ) vList.push_back( (*it) );
|
|
}
|
|
}
|
|
|
|
StructItem* StructInventory::FindJoinablePair( StructItem *pItem )
|
|
{
|
|
if( !pItem->IsJoinable() )
|
|
return NULL;
|
|
|
|
std::vector< StructItem* >::const_iterator it;
|
|
for( it = m_vList.begin(); it != m_vList.end(); ++it )
|
|
{
|
|
if( (*it)->GetItemCode() == pItem->GetItemCode() &&
|
|
(*it)->GetItemEnhance() == pItem->GetItemEnhance() &&
|
|
(*it)->GetItemLevel() == pItem->GetItemLevel() &&
|
|
(*it) != pItem &&
|
|
(*it)->IsJoinable() )
|
|
{
|
|
return (*it);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const __int64 StructInventory::GetItemCount( ItemBase::ItemCode code ) const
|
|
{
|
|
__int64 nCount = 0;
|
|
|
|
std::vector< StructItem* >::const_iterator it;
|
|
|
|
for( it = m_vList.begin(); it != m_vList.end(); ++it )
|
|
{
|
|
if( (*it)->GetItemCode() == code ) nCount += (*it)->GetCount();
|
|
}
|
|
|
|
return nCount;
|
|
}
|
|
|
|
void StructInventory::setCount( struct StructItem* pItem, const __int64 & new_cnt, bool bSkipUpdateItemToDB )
|
|
{
|
|
pItem->SetCount( new_cnt );
|
|
|
|
if( m_pEventReceiver) m_pEventReceiver->onChangeCount( this, pItem, bSkipUpdateItemToDB );
|
|
}
|
|
|
|
void StructInventory::push( struct StructItem* pItem, bool bSkipUpdateItemToDB )
|
|
{
|
|
// 빈 리스트가 아니면 PreviousUID 넣어주기
|
|
if( m_vList.size() > 0 )
|
|
{
|
|
ItemUID lastItemUID = m_vList[ m_vList.size()-1 ]->GetItemUID();
|
|
if( pItem->GetPreviousUID() != lastItemUID )
|
|
{
|
|
pItem->SetPreviousUID( lastItemUID );
|
|
pItem->TurnOnUpdateFlag();
|
|
}
|
|
}
|
|
else // 빈 리스트였다면 이 아이템의 PreviousUID는 0.
|
|
{
|
|
if( pItem->GetPreviousUID() != 0 )
|
|
{
|
|
pItem->SetPreviousUID( 0 );
|
|
pItem->TurnOnUpdateFlag();
|
|
}
|
|
}
|
|
|
|
m_vList.push_back( pItem );
|
|
|
|
if( pItem->IsExpireItem() )
|
|
m_vExpireItemList.push_back( pItem );
|
|
|
|
pItem->SetIdx( (int)( m_vList.size() - 1 ) );
|
|
|
|
if( m_pEventReceiver) m_pEventReceiver->onAdd( this, pItem, bSkipUpdateItemToDB );
|
|
}
|
|
|
|
void StructInventory::pop( struct StructItem* pItem, bool bSkipUpdateItemToDB )
|
|
{
|
|
// PreviousUID 연결해주기
|
|
if( pItem->GetIdx() != static_cast< int >( m_vList.size() - 1 ) )
|
|
{
|
|
StructItem * pNextItem = m_vList[ pItem->GetIdx() + 1 ];
|
|
|
|
ItemUID previousUID = 0;
|
|
if( pItem->GetIdx() != 0 )
|
|
{
|
|
previousUID = m_vList[ pItem->GetIdx() - 1 ]->GetItemUID();
|
|
}
|
|
pNextItem->SetPreviousUID( previousUID );
|
|
pNextItem->TurnOnUpdateFlag();
|
|
}
|
|
|
|
// 한 칸씩 땡겨준다. GetIdx()의 실제 위치 유효성은 앞에 check에서 확인
|
|
for( int i = pItem->GetIdx() ; i < m_vList.size() - 1 ; ++i )
|
|
{
|
|
m_vList[ i ] = m_vList[ i + 1 ];
|
|
m_vList[ i ]->SetIdx( i );
|
|
}
|
|
|
|
// 마지막 원소 제거
|
|
m_vList.pop_back();
|
|
|
|
std::vector< StructItem * >::iterator itErase = std::find( m_vExpireItemList.begin(), m_vExpireItemList.end(), pItem );
|
|
if( itErase != m_vExpireItemList.end() )
|
|
{
|
|
m_vExpireItemList.erase( itErase );
|
|
}
|
|
|
|
if( m_pEventReceiver ) m_pEventReceiver->onRemove( this, pItem, bSkipUpdateItemToDB );
|
|
}
|
|
|
|
const bool StructInventory::_ItemArrangeGreater( const struct StructItem * lhs, const struct StructItem * rhs )
|
|
{
|
|
// 1차 정렬 기준: ItemBase::nArrangeCode
|
|
// 2차 정렬 기준: ItemBase::ITEM_TYPE (ItemBase::nType)
|
|
// 3차 정렬 기준: ItemBase::ItemCode (ItemInstance::Code)
|
|
// 4차 정렬 기준: ItemInstance::nEnhance; 4차 정렬 기준은 내림차순
|
|
|
|
// 1차 정렬 기준은 nArrangeCode 값을 오름차순으로 정리하면 된다.
|
|
int nLHSArrangeCode = lhs->GetItemBase().nArrangeCode;
|
|
int nRHSArrangeCode = rhs->GetItemBase().nArrangeCode;
|
|
|
|
if( nLHSArrangeCode < nRHSArrangeCode )
|
|
return true;
|
|
else if( nLHSArrangeCode > nRHSArrangeCode )
|
|
return false;
|
|
|
|
// 2차 정렬 기준 순서 지정 배열(nType이 배열 인덱스가 되면 정렬 인덱스가 나옴)
|
|
static const int aTypeOrder[] = { 7, 0, 1, 6, 2, 3, 5, 4 };
|
|
static const int nOrderArrayLength = sizeof( aTypeOrder ) / sizeof( int );
|
|
|
|
int nLHSType = lhs->GetItemBase().nType;
|
|
int nRHSType = rhs->GetItemBase().nType;
|
|
|
|
assert( nLHSType >= 0 && nLHSType < nOrderArrayLength );
|
|
assert( nRHSType >= 0 && nRHSType < nOrderArrayLength );
|
|
|
|
// 유효하지 않은 ITEM_TYPE 이면 맨 뒤로 빼버림
|
|
nLHSType = ( nLHSType >= 0 && nLHSType < nOrderArrayLength ) ? aTypeOrder[ nLHSType ] : nOrderArrayLength;
|
|
nRHSType = ( nRHSType >= 0 && nRHSType < nOrderArrayLength ) ? aTypeOrder[ nRHSType ] : nOrderArrayLength;
|
|
|
|
// 2차 정렬 기준: 아이템 타입(오름 차순)
|
|
if( nLHSType < nRHSType )
|
|
return true;
|
|
if( nLHSType > nRHSType )
|
|
return false;
|
|
|
|
// 3차 정렬 기준: 아이템 코드(오름 차순)
|
|
if( lhs->GetItemCode() < rhs->GetItemCode() )
|
|
return true;
|
|
if( lhs->GetItemCode() > rhs->GetItemCode() )
|
|
return false;
|
|
|
|
// 4차 정렬 기준: 아이템 강화 정도(내림 차순)
|
|
if( lhs->GetItemEnhance() > rhs->GetItemEnhance() )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void StructInventory::Arrange()
|
|
{
|
|
std::vector< struct StructItem * > vSortBuffer( m_vList );
|
|
std::sort( vSortBuffer.begin(), vSortBuffer.end(), StructInventory::_ItemArrangeGreater );
|
|
|
|
m_vList.clear();
|
|
|
|
ItemUID previousUID = 0;
|
|
int i = 0;
|
|
for( std::vector< struct StructItem * >::iterator it = vSortBuffer.begin() ; it != vSortBuffer.end() ; ++it )
|
|
{
|
|
StructItem * pItem = (*it);
|
|
pItem->SetIdx( i++ );
|
|
m_vList.push_back( pItem );
|
|
|
|
if( pItem->GetPreviousUID() != previousUID )
|
|
{
|
|
pItem->SetPreviousUID( previousUID );
|
|
pItem->TurnOnUpdateFlag();
|
|
}
|
|
|
|
previousUID = pItem->GetItemUID();
|
|
}
|
|
}
|
|
|
|
void StructInventory::LoadItemList( std::vector< StructItem::INDEX_FINDER > &vItemList, bool isStorage )
|
|
{
|
|
const char * szType = "Login";
|
|
if( isStorage )
|
|
szType = "StorageOpen";
|
|
|
|
std::vector< StructItem::INDEX_FINDER >::iterator it = vItemList.begin();
|
|
StructItem * pItem;
|
|
ItemUID prevUID = 0;
|
|
while( vItemList.begin() != vItemList.end() )
|
|
{
|
|
it = std::find( vItemList.begin(), vItemList.end(), prevUID );
|
|
if( it == vItemList.end() )
|
|
{
|
|
it = vItemList.begin();
|
|
prevUID = 0;
|
|
}
|
|
else
|
|
{
|
|
prevUID = (*it).pItem->GetItemUID();
|
|
}
|
|
|
|
pItem = (*it).pItem;
|
|
StructItem * pExistItem = Push( pItem, pItem->GetCount() );
|
|
|
|
if( pExistItem && pExistItem != pItem )
|
|
{
|
|
// 아이템이 합쳐지면 추적을 위해 로그 남겨준다.
|
|
LOG::Log11N4S( LM_ITEM_JOIN, 0,
|
|
0, pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(),
|
|
pItem->GetItemCode(), pItem->GetCount(),
|
|
pExistItem->GetCount(), 0,
|
|
0, 0,
|
|
pExistItem->GetItemUID(), pItem->GetItemUID(),
|
|
"", 0,
|
|
"", 0,
|
|
"", 0,
|
|
szType, LOG::STR_NTS );
|
|
pExistItem->DBQuery( new DB_UpdateItem( pExistItem ) );
|
|
pItem->SetOwnerInfo( NULL, 0, 0 );
|
|
pItem->DBQuery( new DB_UpdateItemOwner( pItem ) );
|
|
StructItem::PendFreeItem( pItem );
|
|
}
|
|
|
|
vector_fast_erase( &vItemList, it );
|
|
}
|
|
}
|