Files
Leviathan/Server/GameServer/Game/Struct/StructItem.cpp
T
2026-06-01 12:46:52 +02:00

892 lines
23 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <mmo/ArcadiaServer.h>
#include <toolkit/ILock.h>
#include <toolkit/XEnv.h>
#include <toolkit/khash.h>
#include <logging/FileLog.h>
#include "LogClient/LogClient.h"
#include "ErrorCode/ErrorCode.h"
#include "StructItem.h"
#include "GameAllocator.h"
#include "Constant.h"
#include "GameContent.h"
#include "GameDBManager.h"
#include "StructPlayer.h"
#include "DB_Commands.h"
#include "GameRule.h"
#include "StructSummon.h"
#include "StructPet.h"
#include "GameProc.h"
static volatile LONG s_ItemCnt;
static std::vector< ItemBaseServer > s_vItemBase;
static KHash< size_t, hashPr_mod_basic<ItemBase::ItemCode> > s_hsItemCode;
static KHash< size_t, hashPr_string_nocase > s_hsItemName;
static std::vector< StructItem* > s_vPendingItemForDelete;
static XCriticalSection s_lockForItemDelete( "s_lockForItemDelete" );
static KHash< ItemBase::ItemCode, hashPr_mod_basic<ItemBase::ItemCode> > s_hsItemConvertingTable;
static StructItem *s_pDummyItem;
bool StructItem::RegisterItemBase( const ItemBaseServer & base )
{
// 이미 존재하면 수정
if( s_hsItemCode.has( base.nCode ) )
{
size_t idx;
s_hsItemCode.lookup( base.nCode, idx );
s_vItemBase[idx] = base;
return true;
}
s_vItemBase.push_back( base );
s_hsItemCode.add( base.nCode, s_vItemBase.size()-1 );
s_hsItemName.add( GameContent::GetString( base.nNameId ), s_vItemBase.size()-1 );
return true;
}
bool StructItem::RegisterConvertingInfo( ItemBase::ItemCode _key, ItemBase::ItemCode _value )
{
if( s_hsItemConvertingTable.add( _key, _value ) == NULL )
return false;
return true;
}
const char* StructItem::GetName() const
{
return GameContent::GetString( GetItemBase().nNameId );
}
std::string StructItem::GetNameInGame()
{
std::string strName( GameContent::GetString( GetItemBase().nNameId ) );
std::string strGameName;
int cnt = 0;
for( std::string::iterator it = strName.begin() ; it != strName.end() ; ++it )
{
if( (*it) == '\n' || (*it) == '\r' || (*it) == '\n' )
continue;
if( (*it) == '<' )
{
++cnt;
continue;
}
if( (*it) == '>' )
{
--cnt;
continue;
}
if( cnt <= 0 )
strGameName += (*it);
}
return strGameName;
}
bool StructItem::IsValidItemCode( ItemBase::ItemCode code )
{
size_t idx;
if( !s_hsItemCode.lookup( code, idx ) )
{
return false;
}
return true;
}
ItemBaseServer & StructItem::GetItemBase( ItemBase::ItemCode c )
{
static ItemBaseServer gold;
if( !c )
{
return gold;
}
size_t idx;
if( !s_hsItemCode.lookup( c, idx ) )
{
FILELOG( "invalid item code[%d]", c );
return gold;
}
return s_vItemBase[ idx ];
}
ItemBase::ItemCode StructItem::GetItemCode( const char *szItemName )
{
size_t idx;
if( s_hsItemName.lookup( szItemName, idx ) ) return s_vItemBase[idx].nCode;
return 0;
}
StructItem* StructItem::AllocGold( const StructGold & gold,ItemInstance::GenerateCode gcode )
{
return AllocItem( 0, 0, gold.GetRawData(), gcode, 0 );
}
StructItem* StructItem::FindItem( AR_HANDLE handle )
{
return static_cast< StructItem* >( GameObject::raw_get( handle ) );
}
StructItem* StructItem::AllocItem( ItemUID uid,
ItemBase::ItemCode code,
const __int64 & cnt,
ItemInstance::GenerateCode info,
int level,
int enhance,
int flag,
ItemBase::ItemCode socket_0,
ItemBase::ItemCode socket_1,
ItemBase::ItemCode socket_2,
ItemBase::ItemCode socket_3,
int awaken_sid,
int identified_sid,
int remain_time,
const unsigned char & elemental_effect_type,
const time_t & elemental_effect_expire_time,
const int elemental_effect_attack_point,
const int elemental_effect_magic_point,
const ItemBase::ItemCode appearance_code,
int summon_code,
int extra_item_effect) // Fraun Sky Accessories 7/12/2025
{
// 돈 객체를 생성하는 경우 (아이템 코드가 0) cnt가 0일 수 있다.
assert( cnt > 0 || code == 0 );
StructItem* p;
InterlockedIncrement( &s_ItemCnt );
struct _myIntializer : GameAllocateFunctor
{
virtual void operator()( void * pObj, AR_HANDLE handle )
{
new (pObj) StructItem( handle );
}
};
AR_HANDLE handle = allocItemStruct( &p, _myIntializer() );
// _oprint( "ALLOC ITEM : %08X\n", p );
if( handle == 0 )
{
s_pDummyItem = p;
handle = allocItemStruct( &p, _myIntializer() );
}
//new (p) StructItem( handle );
assert( code >= 0 );
p->m_pItemBase = &GetItemBase( code );
p->m_Instance.UID = uid;
p->m_Instance.Code = code;
p->m_Instance.PreviousUID = 0;
p->m_Instance.nCount = cnt;
p->m_Instance.nLevel = ( level == -1 ) ? p->m_pItemBase->nLevel : level ; // If not set, use the value from ItemResource
if( p->m_Instance.nLevel <= 0 )
{
// Money can have level 0 since it's not a real item, it doesn't matter either way
assert( p->m_pItemBase->nCode == 0 );
p->m_Instance.nLevel = 1;
}
p->m_Instance.nEnhance = ( enhance == -1 ) ? p->m_pItemBase->nEnhance : enhance ; // If not set, use the value from ItemResource
if( p->m_Instance.nEnhance < 0 )
{
assert( 0 );
p->m_Instance.nEnhance = 0;
}
if( p->m_pItemBase->nGroup == ItemBase::GROUP_SKILLCARD && !p->m_Instance.nEnhance ) p->m_Instance.nEnhance = 1; // Skill cards have a default value of 1
p->m_Instance.GenerateInfo = info;
p->m_Instance.nWearInfo = static_cast< ItemBase::ItemWearType >( -1 );
p->m_Instance.OwnerHandle = 0;
p->m_Instance.nOwnerUID = 0;
p->m_Instance.Socket[0] = socket_0;
p->m_Instance.Socket[1] = socket_1;
p->m_Instance.Socket[2] = socket_2;
p->m_Instance.Socket[3] = socket_3;
p->m_Instance.RandomOption[ ItemInstance::AWAKEN ].nSID = awaken_sid;
p->m_Instance.RandomOption[ ItemInstance::IDENTIFIED ].nSID = identified_sid;
p->m_Instance.cElementalEffectType = elemental_effect_type;
p->m_Instance.tElementalEffectExpire = elemental_effect_expire_time;
p->m_Instance.nElementalEffectAttackPoint = elemental_effect_attack_point;
p->m_Instance.nElementalEffectMagicPoint = elemental_effect_magic_point;
p->m_Instance.nAppearanceCode = appearance_code;
p->m_Instance.nSummonCode = summon_code;
p->m_Instance.nAdditionalItemEffect = extra_item_effect; // Fraun Sky Accessories 7/12/2025
p->m_Instance.nCurrentEtherealDurability = ( p->IsEtherealStone() ) ? 0 : p->GetItemBase().nEtherealDurability;
p->m_Instance.nCurrentEndurance = p->GetItemBase().nEndurance;
p->m_hBindedTarget = 0;
p->GetInstanceFlag().CopyFrom( ( flag == -1 ) ? &p->m_pItemBase->nInstanceFlag : &flag );
if( remain_time == -1 )
{
if( p->IsExpireItem() )
{
p->m_Instance.tExpire = time( NULL ) + p->GetItemBase().available_time;
}
else
{
p->m_Instance.tExpire = 0;
}
}
else
{
if( p->GetItemBase().decrease_type == ItemBase::DECREASE_ALWAYS )
{
p->m_Instance.tExpire = remain_time;
}
else
{
p->m_Instance.tExpire = time( NULL ) + remain_time;
}
}
if( !p->IsJoinable() && !p->IsGold() && p->m_Instance.nCount > 1 )
p->m_Instance.nCount = 1;
// Item conversion table. Converts old codes to the designated codes
if( s_hsItemConvertingTable.has( code ) )
{
ItemBase::ItemCode nCode = -1;
s_hsItemConvertingTable.lookup( code, nCode );
p->m_Instance.Code = nCode;
p->m_pItemBase = &GetItemBase( nCode );
if( uid )
{
// If a UID exists, we need to change the value in the DB as well, so a log is left.
// Inside AllocItem, there's no owner information, so we can't record the n1, n2 columns (which store the item's owner info) — that's a drawback..
LOG::Log11N4S( LM_ITEM_CONVERT, 0,
0, p->GetItemEnhance() * 100 + p->GetItemLevel(),
code, nCode,
p->GetCount(), 0,
0, 0,
0, p->GetItemUID(),
"", 0,
"", 0,
"", 0,
"ITEM_CONVERT_BY_ALLOC_ITEM", LOG::STR_NTS );
p->DBQuery( new DB_UpdateItemCode( p ) );
}
}
return p;
}
void StructItem::PendFreeItem_( StructItem* p, const char* function, const int line )
{
static int cnt;
static AR_TIME prev_proc_time = GetArTime();
if( function != NULL )
{
p->m_deleteFunction = function;
p->m_deleteLine = line;
}
// 소환수/펫 아이템이 삭제될 때 함께 삭제되도록 ArcadiaServer::Instance().DeleteObject 를 호출하려면 s_lockForItemDelete가 풀린 상태여야 함
StructCreature * pSummonPet = NULL;
{
THREAD_SYNCRONIZE( &s_lockForItemDelete );
//_oprint( "PEND FREE ITEM : %08X\n", p );
if( std::find( s_vPendingItemForDelete.begin(), s_vPendingItemForDelete.end(), p ) != s_vPendingItemForDelete.end() )
{
assert( 0 );
return;
}
s_vPendingItemForDelete.push_back( p );
p->bIsDeleteRequested = true;
// 소환수 카드이고 테이밍 되어 있던 카드일 경우 소환수 제거
if( p->IsSummonCard() && p->GetSummonStruct() )
{
// 역소환 처리가 된 후에 아이템을 삭제해야 하지만, 어디선가 소환된 소환수의 카드를 삭제 시도한 경우
if( p->GetSummonStruct()->IsInWorld() )
{
assert( 0 );
// 급한 대로 월드에서 제거
RemoveSummonFromWorld( p->GetSummonStruct() );
}
pSummonPet = p->GetSummonStruct();
}
// 펫 카드일 경우 소속되어 있던 펫 제거
else if( p->IsPetCage() && p->GetPetStruct() )
{
// 역소환 처리가 된 후에 아이템을 삭제해야 하지만, 어디선가 소환된 소환수의 카드를 삭제 시도한 경우
if( p->GetPetStruct()->IsInWorld() )
{
assert( 0 );
// 급한 대로 월드에서 제거
RemovePetFromWorld( p->GetPetStruct() );
}
pSummonPet = p->GetPetStruct();
}
#ifdef _DEBUG
// 임시
deletePendingItem();
#endif // _DEBUG
if( ++cnt > 10 )
{
cnt = 0;
AR_TIME t = GetArTime();
if( prev_proc_time + 100 < t )
{
prev_proc_time = t;
deletePendingItem();
}
}
}
if( pSummonPet )
{
ArcadiaServer::Instance().DeleteObject( pSummonPet );
}
}
void StructItem::deletePendingItem()
{
std::vector< StructItem* > vTmp;
std::vector< StructItem* >::iterator it;
for( it = s_vPendingItemForDelete.begin(); it != s_vPendingItemForDelete.end(); ++it )
{
if( !(*it)->IsDeleteable() )
{
vTmp.push_back( *it );
continue;
}
//_oprint( "DELETE ITEM : %08X\n", (*it) );
freeItem( *it );
}
s_vPendingItemForDelete.swap( vTmp );
}
void StructItem::InitItemSystem()
{
ENV().Bind( "game.item_count", (int*)&s_ItemCnt );
}
void StructItem::DeInitItemSystem()
{
if( s_pDummyItem )
freeItemStruct( s_pDummyItem );
THREAD_SYNCRONIZE( &s_lockForItemDelete );
while( !s_vPendingItemForDelete.empty() ) deletePendingItem();
}
void StructItem::freeItem( StructItem* p )
{
// _oprint( "DEL ITEM : %08X\n", p );
InterlockedDecrement( &s_ItemCnt );
prepareFreeItemStruct( p );
p->StructItem::~StructItem();
//memset( p, 0xaa, sizeof(StructItem) ); // 임시, 디버깅용
freeItemStruct( p );
}
bool StructItem::SetItemUID( ItemUID uid )
{
if( m_Instance.UID ) return false;
m_Instance.UID = uid;
return true;
}
bool StructItem::ChangeItemCode( ItemBase::ItemCode code )
{
// 잘못 된 아이템 코드일 경우 안 바꿔줌
ItemBaseServer *pBase = &GetItemBase( code );
if( pBase->nCode == 0 )
return false;
m_pItemBase = pBase;
m_Instance.Code = code;
// 업데이트 플래그는 건들지않는다. (위 단 핸들러에서 판단)
DBQuery( new DB_UpdateItemCode( this ) );
return true;
}
StructItem::StructItem( AR_HANDLE handle )
: m_bQueryLock( "StructItem::m_bQueryLock" )
{
m_nArObjectType = ArObject::STATIC_OBJECT;
m_bIsVirtualItem = false;
m_bIsEventDrop = false;
m_Instance.nLevel = 1;
m_Instance.nEnhance = 1;
m_hHandle = handle;
m_nAccountID = 0;
//m_nOwnSummonUID = -1;
m_unInventoryIndex = 0; // 리스트에서의 위치
m_nDBUpdateFlag = 0;
m_nDropTime = 0;
m_pSummon = NULL;
m_pPet = NULL;
m_lQueryList.clear();
memset( &m_ItemPickupOrder, 0, sizeof( m_ItemPickupOrder ) );
#ifdef _MEM_USAGE_DEBUG
XSEH::IncreaseAllocCount( "StructItem" );
#endif
m_deleteFunction = NULL;
m_deleteLine = 0xFFFFFFFF;
}
StructItem::~StructItem()
{
#ifdef _MEM_USAGE_DEBUG
XSEH::DecreaseAllocCount( "StructItem" );
#endif
}
bool StructItem::ProcDelete()
{
if( bIsDeleted ) assert( 0 );
StructItem::freeItem( this );
return true;
}
bool StructItem::IsWearable() const
{
// 장착 불가 아이템
if( GetWearType() == ItemBase::WEAR_CANTWEAR && !IsChaosStone() )
return false;
// 강화 실패작
if( GetInstanceFlag().IsOn( ItemInstance::ITEM_FLAG_FAILED ) )
return false;
// 에테리얼 내구도 소모로 인한 파괴
if( GetMaxEtherealDurability() > 0 && GetCurrentEtherealDurability() <= 0 )
return false;
// 랜덤 옵션 아이템은 오픈하지 않았다면 장착 불가
if( IsRandomizable() && !IsIdentified() )
{
return false;
}
return true;
}
int StructItem::GetMaxSocketCount() const
{
int nSocketCount = GetItemBase().nSocketCount;
if( IsRandomizable() )
{
if( IsIdentified() )
{
// 밸트 소켓과 랜덤옵션의 소켓과는 다르다.
if( IsBelt() == false )
{
for( int i = 0 ; i < ItemInstance::MAX_RANDOM_OPTION_NUMBER ; ++i )
{
if( GetIdentifiedOptionType( i ) == ITEM_EFFECT_PASSIVE::INC_SOCKET_COUNT_A || GetIdentifiedOptionType( i ) == ITEM_EFFECT_PASSIVE::INC_SOCKET_COUNT_B )
{
nSocketCount += GetIdentifiedOptionValue1( i );
}
}
if( nSocketCount > ItemBase::MAX_SOCKET_NUMBER )
nSocketCount = ItemBase::MAX_SOCKET_NUMBER;
}
}
else
{
// 미확인 랜덤 옵션
nSocketCount = 0;
}
}
return nSocketCount;
}
int StructItem::GetUsingSocketCount() const
{
int nUsableSocketNumber = GetMaxSocketCount();
if( !nUsableSocketNumber )
return 0;
int nCount = 0;
for( int i = 0 ; i < nUsableSocketNumber ; ++i )
{
ItemBase::ItemCode nSocketCode = GetSocketCode( i );
if( nSocketCode )
++nCount;
}
return nCount;
}
// Instance에 박힌 SummonCode 우선 (베이스, 인스턴스 서몬 코드 둘다 있으면 인스턴스 정보로 줌)
int StructItem::GetSummonCode() const
{
if( m_Instance.nSummonCode )
return m_Instance.nSummonCode;
return m_pItemBase->nSummonId;
}
void StructItem::SetItemEnhance( int enhance )
{
if( m_Instance.nEnhance != enhance )
{
m_Instance.nEnhance = enhance;
GameObject* object = GameObject::raw_get( GetOwnerHandle() );
if( object && object->IsPlayer() )
{
static_cast< StructPlayer* >( object )->UpdateQuestStatusByItemEnhance( GetItemCode() );
}
TurnOnUpdateFlag();
}
}
const int StructItem::ProcEtherealDurabilityConsumption( const int nBaseConsumption, const c_fixed10 & fConsumeRate, const c_fixed10 & fEnvironmentalConsumeRate )
{
// 상급 아이템이 아니거나 에테리얼 내구도가 처음부터 0이거나 남은 에테리얼 내구도가 0이면 불가
// 혹은 벨트 장착용 보스 카드
if( ( !( GetItemGrade() && GetMaxEtherealDurability() && GetCurrentEtherealDurability() ) && !IsEquipmentOnBelt() ) || GameRule::bItemDurabilitySwitch) // Credits to AziaMafia: Durability consumption switch
return 0;
// 소모율 계산 마무리 + 기본 소모량 적용
c_fixed10 fConsumption( fConsumeRate + GameRule::GetEtherealDurabilityConsumeRateByItem( GetItemRank(), GetItemGrade() ) );
fConsumption *= nBaseConsumption;
// 상황에 따른 소모율 적용
fConsumption *= fEnvironmentalConsumeRate;
fConsumption *= std::min( 1 / GameRule::nEtherealDurabilityConsumptionRate, 1.f );
// 소모될 에테리얼 내구도가 0 이하면 패스
if( static_cast< int >( fConsumption ) <= 0 )
return 0;
int nPrevEtherealDurability = GetCurrentEtherealDurability();
AddCurrentEtherealDurability( -1 * fConsumption );
return nPrevEtherealDurability - GetCurrentEtherealDurability();
}
const int StructItem::GetMaxEtherealDurability() const
{
float pEnhanceDurability[ 30 ] =
{
1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f,
2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.f, 10.f, 10.f, 10.f, 10.f
};
// 크리처 카드는 강화 정도에 따라 내구도가 다르다.
if( IsSummonCard() )
return GameContent::GetCreatureEnhanceInfo( GetItemEnhance() )->card_durability;
if( IsEquipment() && GetItemEnhance() > 0 && GetItemEnhance() <= 29 )
return GetItemBase().nEtherealDurability * pEnhanceDurability[ GetItemEnhance() - 1 ];
return GetItemBase().nEtherealDurability;
}
void StructItem::SetCurrentEndurance( int n )
{
int nPrevEndurance = m_Instance.nCurrentEndurance;
m_Instance.nCurrentEndurance = std::max( std::min( n, GetMaxEndurance() ), 0 );
if( nPrevEndurance != m_Instance.nCurrentEndurance )
{
TurnOnUpdateFlag();
}
}
int StructItem::GetMaxEndurance() const
{
int nUsableSocketNumber = GetMaxSocketCount();
if( !nUsableSocketNumber )
return GetItemBase().nEndurance;
int nMaxEndurance = 0;
for( int i = 0 ; i < nUsableSocketNumber ; ++i )
{
ItemBase::ItemCode nSocketCode = GetSocketCode( i );
if( !nSocketCode )
continue;
nMaxEndurance += GetItemBase( nSocketCode ).nEndurance;
}
return ( nMaxEndurance ) ? nMaxEndurance : GetItemBase().nEndurance;
}
int StructItem::GetLevelLimit() const
{
return std::max( GameRule::GetItemLevelLimitByRank( GetItemRank() ), GetItemBase().nMinLevel );
}
int StructItem::GetRecommendLevel() const
{
return GameRule::GetItemRecommendLevel( GetItemRank(), GetItemLevel(), GetItemBase().nMinLevel );
}
void StructItem::SetInstanceFlagOn( int idx )
{
if( m_Instance.Flag.IsOn( idx ) )
return;
TurnOnUpdateFlag();
m_Instance.Flag.On( idx );
}
void StructItem::SetInstanceFlagOff( int idx )
{
if( !m_Instance.Flag.IsOn( idx ) )
return;
TurnOnUpdateFlag();
m_Instance.Flag.Off( idx );
}
void StructItem::SetBindTarget( struct StructCreature *pTarget, const bool bSkipDBUpdate )
{
m_Instance.Socket[0] = pTarget ? ( pTarget->IsPlayer() ? pTarget->GetSID() : 0 ) : 0;
m_Instance.Socket[1] = pTarget ? ( pTarget->IsSummon() ? pTarget->GetSID() : 0 ) : 0;
m_hBindedTarget = pTarget ? pTarget->GetHandle() : 0;
if( !bSkipDBUpdate )
TurnOnUpdateFlag();
}
// DB 에 아이템의 소유자를 변경해야 하는 쿼리가 미결되었는데 덜썩 지워버리면 낭패이므로
// 쿼리가 다 끝나기 전에는 삭제되지 않도록 한다.
bool StructItem::IsDeleteable()
{
THREAD_SYNCRONIZE( m_bQueryLock );
if( !GameObject::IsDeleteable() ) return false;
if( m_lQueryList.empty() ) return true;
return false;
}
void StructItem::DBQuery( GameDBManager::DBProc *pWork )
{
THREAD_SYNCRONIZE( m_bQueryLock );
if( m_lQueryList.empty() )
{
DB().Push( pWork );
}
m_lQueryList.push_back( pWork );
}
void StructItem::onEndQuery()
{
THREAD_SYNCRONIZE( m_bQueryLock );
m_lQueryList.pop_front();
if( !m_lQueryList.empty() )
{
DB().Push( m_lQueryList.front() );
}
}
void StructItem::CopyFrom( StructItem* pFrom )
{
AR_HANDLE hOldOwnerHandle = m_Instance.OwnerHandle;
int nOldOwner = m_Instance.UID;
mv = pFrom->mv;
layer = pFrom->layer;
m_Instance = pFrom->m_Instance;
m_Instance.UID = 0;
m_Instance.nOwnerUID = nOldOwner;
m_Instance.OwnerHandle = hOldOwnerHandle;
}
const bool StructItem::IsReplaceable( const ItemBase::ItemCode code, const bool reset_remain_time )
{
// 동종의 아이템은 무조건 실패(시간 제한 재설정은 SetRemainTime 함수를 이용해야 함)
if( code == m_Instance.Code )
return false;
ItemBaseServer *pNewItemBase = &GetItemBase( code );
// 기존에 시간 제한이 있던 아이템이었을 경우 영구템으로 변경하는데 남은 시간을 초기화하지 않아야하는 경우라면 불가능
if( IsExpireItem() && !reset_remain_time && pNewItemBase->decrease_type == ItemBase::PERMANENT )
{
return false;
}
// 중첩 가능 아이템을 중첩 불가 아이템으로 바꾸는데 기존 수량이 1개가 아니면 불가능
if( !pNewItemBase->Flag.IsOn( ItemBase::FLAG_JOIN ) && GetCount() != 1 )
return false;
// 아이템 생성 당시 기본 InstanceFlag 가 다른 아이템으로는 변경 불가능(아이템 생성 이후 어떻게 바뀌었는지가 날아감 -_ -;)
if( pNewItemBase->nInstanceFlag != GetItemBase().nInstanceFlag )
return false;
return true;
}
bool StructItem::IsExpireItem() const
{
if( GetItemBase().decrease_type == ItemBase::DECREASE_ON_GAME || GetItemBase().decrease_type == ItemBase::DECREASE_ALWAYS )
return true;
return false;
}
const unsigned short StructItem::SetElementalEffect( const unsigned char & cType, const time_t & tExpire )
{
if( !IsWeapon() )
{
return RESULT_NOT_ACTABLE;
}
m_Instance.cElementalEffectType = cType;
m_Instance.tElementalEffectExpire = tExpire;
// 속성 이펙트 적용 시 기존에 있던 추가 성능은 제거됨
m_Instance.nElementalEffectAttackPoint = 0;
m_Instance.nElementalEffectMagicPoint = 0;
return RESULT_SUCCESS;
}
const unsigned short StructItem::SetElementalEffectAttackPoint( const int nAttackPoint )
{
if( !IsWeapon() || !GetElementalEffectType() )
{
return RESULT_NOT_ACTABLE;
}
// 속성 이펙트 추가 공격력 설정
m_Instance.nElementalEffectAttackPoint = nAttackPoint;
return RESULT_SUCCESS;
}
const unsigned short StructItem::SetElementalEffectMagicPoint( const int nMagicPoint )
{
if( !IsWeapon() || !GetElementalEffectType() )
{
return RESULT_NOT_ACTABLE;
}
// 속성 이펙트 추가 마력 설정
m_Instance.nElementalEffectMagicPoint = nMagicPoint;
return RESULT_SUCCESS;
}
void StructItem::ClearElementalEffect()
{
// 속성 이펙트 초기화
m_Instance.cElementalEffectType = 0;
m_Instance.tElementalEffectExpire = 0;
// 추가 공격력/마력 성능 초기화
m_Instance.nElementalEffectAttackPoint = 0;
m_Instance.nElementalEffectMagicPoint = 0;
}
bool StructItem::SetRandomOption( ItemInstance::RANDOM_TYPE eType, ItemInstance::RANDOM_OPTION & RandomOption )
{
SetRandomSID( eType, RandomOption.nSID );
m_Instance.RandomOption[ eType ].nRandomType = eType;
for( int nCnt = 0 ; nCnt < ItemInstance::MAX_RANDOM_OPTION_NUMBER; ++nCnt )
{
m_Instance.RandomOption[ eType ].OptionInfo[ nCnt ].nType = RandomOption.OptionInfo[ nCnt ].nType;
m_Instance.RandomOption[ eType ].OptionInfo[ nCnt ].fValue1 = RandomOption.OptionInfo[ nCnt ].fValue1;
m_Instance.RandomOption[ eType ].OptionInfo[ nCnt ].fValue2 = RandomOption.OptionInfo[ nCnt ].fValue2;
}
return true;
}
ItemInstance::RANDOM_OPTION* StructItem::GetRandomOption( ItemInstance::RANDOM_TYPE eType )
{
if( eType < 0 || eType >= ItemInstance::MAX )
return NULL;
return &( m_Instance.RandomOption[ eType ] );
}
// Fraun Sky Accessories 7/12/2025
bool StructItem::ChangeAdditionalItemEffect(ItemBase::ItemCode code, int nItemEffectID)
{
ItemBaseServer* pBase = &GetItemBase(code);
if (pBase->nCode == 0)
return false;
m_pItemBase = pBase;
m_Instance.Code = code;
m_Instance.nAdditionalItemEffect = nItemEffectID;
DBQuery(new DB_UpdateItemEffect(this));
return true;
}