3188 lines
140 KiB
C++
3188 lines
140 KiB
C++
|
||
#include <vector>
|
||
|
||
#include <toolkit/XEnv.h>
|
||
#include <mmo/ArcadiaServer.h>
|
||
#include <logging/FileLog.h>
|
||
|
||
#include "LogClient/LogClient.h"
|
||
#include "ErrorCode/ErrorCode.h"
|
||
|
||
#include "MixManager.h"
|
||
#include "StructItem.h"
|
||
#include "StructPlayer.h"
|
||
#include "StructSummon.h"
|
||
#include "StructSkill.h"
|
||
#include "DB_Commands.h"
|
||
#include "SendMessage.h"
|
||
#include "GameMessage.h"
|
||
#include "GameContent.h"
|
||
#include "RandomManager.h"
|
||
|
||
#include <lua/lua.hpp>
|
||
|
||
static std::vector< MixBase > s_vMixInfo;
|
||
static std::vector< EnhanceInfo > s_vEnhanceInfo;
|
||
static std::vector< DecomposeBase > s_vDecomposeInfo;
|
||
|
||
void MixManager::procEnhanceFail( StructPlayer *pPlayer, StructItem *pItem, int nFailResult )
|
||
{
|
||
if( nFailResult == EnhanceInfo::RESULT_SKILL_CARD_FAIL )
|
||
{
|
||
if( pItem->GetItemEnhance() > 3 )
|
||
{
|
||
// 4강 이상인데 실패한 경우 -3강짜리 카드 만들어서 하나 넣어준다.
|
||
StructItem * pNewItem = StructItem::AllocItem( 0, pItem->GetItemCode(), 1 );
|
||
pNewItem->CopyFrom( pItem );
|
||
pNewItem->SetIdx( 0 );
|
||
pNewItem->SetCount( 1 );
|
||
pNewItem->SetItemEnhance( pNewItem->GetItemEnhance() - 3 );
|
||
pPlayer->PushItem( pNewItem, 1 );
|
||
}
|
||
|
||
pPlayer->EraseItem( pItem, 1 );
|
||
}
|
||
else if( nFailResult == EnhanceInfo::RESULT_FAIL )
|
||
{
|
||
if( pItem->GetItemEnhance() >= ENV().GetInt( "game.enhance_destroy_level", 0 ) )
|
||
{
|
||
pItem->SetInstanceFlagOn( ItemInstance::ITEM_FLAG_FAILED );
|
||
}
|
||
//else if( pItem->GetItemEnhance() > 12 )
|
||
//{
|
||
// pItem->SetInstanceFlagOn( ItemInstance::ITEM_FLAG_FAILED );
|
||
//}
|
||
|
||
// 벨트의 소켓은 다른 용도로 쓰이므로 복구가 가능하도록 원래 값을 유지한다.
|
||
if( pItem->GetItemClass() != ItemBase::CLASS_BELT && pItem->GetInstanceFlag().IsOn( ItemInstance::ITEM_FLAG_FAILED ) )
|
||
{
|
||
for( int i = 0 ; i < ItemBase::MAX_SOCKET_NUMBER ; ++i )
|
||
{
|
||
pItem->SetSocketCode( i, 0 );
|
||
}
|
||
}
|
||
|
||
pItem->DBQuery( new DB_UpdateItem( pItem ) );
|
||
SendItemMessage( pPlayer, pItem );
|
||
}
|
||
else if( nFailResult == EnhanceInfo::RESULT_ACCESSORY_FAIL )
|
||
{
|
||
if( pItem->GetItemEnhance() <= 3 )
|
||
{
|
||
pItem->SetItemEnhance( 0 );
|
||
// DB 업데이트, SendItemMessage는 이하에서 담당
|
||
pItem = pPlayer->onAfterChangeItemProperty( pItem );
|
||
}
|
||
else
|
||
{
|
||
pItem->SetItemEnhance( pItem->GetItemEnhance() - 3 );
|
||
// DB 업데이트, SendItemMessage는 이하에서 담당
|
||
pItem = pPlayer->onAfterChangeItemProperty( pItem );
|
||
}
|
||
}
|
||
else if( nFailResult == EnhanceInfo::RESULT_DO_NOTHING )
|
||
{
|
||
// 따로 실패에 대한 패널티 처리 안 함. (혹시 이걸 호출한 함수에서 pItem에 무슨 짓을 했을수도 있으니 저장만 해주자.)
|
||
pPlayer->onAfterChangeItemProperty( pItem );
|
||
}
|
||
}
|
||
|
||
void MixManager::RegisterMixInfo( const MixBase & info )
|
||
{
|
||
for( std::vector< MixBase >::iterator it = s_vMixInfo.begin(); it != s_vMixInfo.end(); ++it )
|
||
{
|
||
if( (*it).id == info.id )
|
||
{
|
||
(*it) = info;
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
s_vMixInfo.push_back( info );
|
||
}
|
||
|
||
void MixManager::RegisterDecomposeInfo( const DecomposeBase & info )
|
||
{
|
||
for( std::vector< DecomposeBase >::iterator it = s_vDecomposeInfo.begin(); it != s_vDecomposeInfo.end(); ++it )
|
||
{
|
||
if( (*it).id == info.id )
|
||
{
|
||
(*it) = info;
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
s_vDecomposeInfo.push_back( info );
|
||
}
|
||
|
||
// 조합 재료 체크 37번, 기획자가 정한 특수 유형들은 여기서 체크 (데이터로 제어하지 않고, 기획자의 요청에 따라 하드코딩 됨)
|
||
bool check_designer_defined_type( const StructItem *pItem, int nType )
|
||
{
|
||
switch( nType )
|
||
{
|
||
case DESIGNER_TYPE_WEAPON:
|
||
{
|
||
if( pItem->GetItemGroup() == ItemBase::GROUP_WEAPON )
|
||
return true;
|
||
break;
|
||
}
|
||
|
||
case DESIGNER_TYPE_ARMOR:
|
||
{
|
||
int nGroup = pItem->GetItemGroup();
|
||
if( nGroup == ItemBase::GROUP_ARMOR || nGroup == ItemBase::GROUP_SHIELD ||
|
||
nGroup == ItemBase::GROUP_HELM || nGroup == ItemBase::GROUP_GLOVE ||
|
||
nGroup == ItemBase::GROUP_BOOTS || nGroup == ItemBase::GROUP_BELT ||
|
||
nGroup == ItemBase::GROUP_MANTLE )
|
||
return true;
|
||
break;
|
||
}
|
||
|
||
case DESIGNER_TYPE_DURABILITY:
|
||
{
|
||
int nGroup = pItem->GetItemGroup();
|
||
if( nGroup == ItemBase::GROUP_WEAPON || nGroup == ItemBase::GROUP_ARMOR ||
|
||
nGroup == ItemBase::GROUP_SHIELD || nGroup == ItemBase::GROUP_HELM ||
|
||
nGroup == ItemBase::GROUP_GLOVE || nGroup == ItemBase::GROUP_BOOTS ||
|
||
nGroup == ItemBase::GROUP_BELT || nGroup == ItemBase::GROUP_ACCESSORY )
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
default: return false;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
bool check_material_info( const MaterialInfo & info, const StructItem * pItem, unsigned short *pItemCount )
|
||
{
|
||
bool bIsCountChecked = false;
|
||
|
||
// 최초 체크 조건이 0(없음)이면 아무 조건도 없는 것이 아니라 아이템이 들어오믄 안됨
|
||
if( !info.type[0] )
|
||
{
|
||
return !pItem;
|
||
}
|
||
|
||
// 아이템이 없다면 조건 체크에 들어오면 안 됨
|
||
if( !pItem )
|
||
{
|
||
return false;
|
||
}
|
||
|
||
for( int i = 0; i < MaterialInfo::MATERIAL_INFO_COUNT ; ++i )
|
||
{
|
||
//_cprint("Current i: %i; type: %i; value: %i\n", i, info.type[i], info.value[i]);
|
||
//_oprint("Checking %i: type is %i, value is %i\n", i, info.type[i], info.value[i]);
|
||
switch( info.type[i] )
|
||
{
|
||
case MIX_CHECK_ITEM_GROUP:
|
||
{
|
||
//_oprint("MIX_CHECK_ITEM_GROUP: item Group: %i; compare to: %i: %s\n", pItem->GetItemGroup(), info.value[i], (pItem->GetItemGroup() != info.value[i] ? "true" : "false"));
|
||
if (pItem->GetItemGroup() != info.value[i])
|
||
{
|
||
return false;
|
||
|
||
}
|
||
}
|
||
break;
|
||
|
||
case MIX_CHECK_ITEM_CLASS:
|
||
if( pItem->GetItemClass() != info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ITEM_WEAR_TYPE:
|
||
if (pItem->GetWearType() != info.value[i])
|
||
return false;
|
||
break;
|
||
|
||
|
||
case MIX_CHECK_ITEM_ID:
|
||
{
|
||
//_oprint("MIX_CHECK_ITEM_ID: item ID: %i; compare to: %i: %s\n", pItem->GetItemCode(), info.value[i], (pItem->GetItemCode() != info.value[i] ? "true" : "false"));
|
||
if (pItem->GetItemCode() != info.value[i])
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case MIX_CHECK_ITEMID_EQUAL_OR_MORE:
|
||
if (pItem->GetItemCode() < info.value[i])
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ITEMID_EQUAL_OR_LESS:
|
||
if (pItem->GetItemCode() > info.value[i])
|
||
return false;
|
||
break;
|
||
|
||
|
||
case MIX_CHECK_ITEM_RANK:
|
||
if( pItem->GetItemRank() != info.value[i] )
|
||
return false;
|
||
break;
|
||
|
||
case MIX_CHECK_ITEM_LEVEL:
|
||
if( pItem->GetItemLevel() != info.value[i] )
|
||
return false;
|
||
break;
|
||
|
||
case MIX_CHECK_INSTANCE_FLAG_ON:
|
||
if( !pItem->GetInstanceFlag().IsOn( info.value[i] ) )
|
||
return false;
|
||
break;
|
||
|
||
case MIX_CHECK_INSTANCE_FLAG_OFF:
|
||
if( pItem->GetInstanceFlag().IsOn( info.value[i] ) )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ENHANCE_MATCH:
|
||
if( pItem->GetItemEnhance() != info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ENHANCE_DISMATCH:
|
||
if( pItem->GetItemEnhance() == info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ITEM_COUNT:
|
||
bIsCountChecked = true;
|
||
if( (*pItemCount) != info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ELEMENTAL_EFFECT_MATCH:
|
||
{
|
||
// 비트셋으로 value를 지정하므로 무속성: 0, 그 외의 속성: 2^(속성값 - 1)
|
||
// 이를 위해서 2^(속성값) / 2로 처리함(무속성은 2^0 = 1 이므로 int로 캐스팅 후에 2로 나누어(= 1비트 Right-Shift) 0이 됨)
|
||
int nItemElementalEffectBitflag = static_cast< int >( pow( 2.0, pItem->GetElementalEffectType() ) ) >> 1;
|
||
|
||
// 아이템의 속성이 무속성이라면
|
||
if( !nItemElementalEffectBitflag )
|
||
{
|
||
// 체크 조건 값도 무속성인 경우에만 통과
|
||
if( info.value[i] )
|
||
return false;
|
||
}
|
||
// 무속성이 아니라면 체크 조건 값과 비트 연산 체크
|
||
else if( ( nItemElementalEffectBitflag & info.value[i] ) != nItemElementalEffectBitflag )
|
||
return false;
|
||
}
|
||
break;
|
||
case MIX_CHECK_ELEMENTAL_EFFECT_MISMATCH:
|
||
{
|
||
// 비트셋으로 value를 지정하므로 무속성: 0, 그 외의 속성: 2^(속성값 - 1)
|
||
// 이를 위해서 2^(속성값) / 2로 처리함(무속성은 2^0 = 1 이므로 int로 캐스팅 후에 2로 나누어(= 1비트 Right-Shift) 0이 됨)
|
||
int nItemElementalEffectBitflag = static_cast< int >( pow( 2.0, pItem->GetElementalEffectType() ) ) >> 1;
|
||
|
||
// 아이템의 속성이 무속성이라면
|
||
if( !nItemElementalEffectBitflag )
|
||
{
|
||
// 체크 조건 값이 무속성이 아닌 경우에만 통과
|
||
if( !info.value[i] )
|
||
return false;
|
||
}
|
||
// 무속성이 아니라면 체크 조건 값과 비트 연산 체크
|
||
else if( ( nItemElementalEffectBitflag & info.value[i] ) == nItemElementalEffectBitflag )
|
||
return false;
|
||
}
|
||
break;
|
||
case MIX_CHECK_ITEM_WEAR_POSITION_MATCH:
|
||
if( pItem->GetWearType() != info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ITEM_WEAR_POSITION_MISMATCH:
|
||
if( pItem->GetWearType() == info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ITEM_COUNT_GE:
|
||
bIsCountChecked = true;
|
||
if( (*pItemCount) < info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ITEM_ETHEREAL_DURABILITY_E:
|
||
if( pItem->GetCurrentEtherealDurability() != info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ITEM_ETHEREAL_DURABILITY_NE:
|
||
if( pItem->GetCurrentEtherealDurability() == info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ITEM_GRADE:
|
||
if( pItem->GetItemGrade() != info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ITEM_EXPIRED_TIME_GE:
|
||
if( info.value[i] == -1 && pItem->IsExpireItem() ) return false;
|
||
if( info.value[i] != -1 && pItem->IsExpireItem() && pItem->GetExpireTime() < info.value[i] ) return false;
|
||
break;
|
||
case MIX_CHECK_ITEM_EXPIRED_TIME_LE:
|
||
if( info.value[i] == -1 && pItem->IsExpireItem() ) return false;
|
||
if( info.value[i] != -1 && ( !pItem->IsExpireItem() || pItem->GetExpireTime() > info.value[i] ) ) return false;
|
||
break;
|
||
|
||
case MIX_CHECK_ITEM_FIRST_SOCKET_CODE_MATCH:
|
||
if( pItem->GetSocketCode( 0 ) != info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ITEM_MAX_ETHEREAL_DURABILITY_E:
|
||
if( pItem->GetMaxEtherealDurability() != info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ITEM_MAX_ETHEREAL_DURABILITY_NE:
|
||
if( pItem->GetMaxEtherealDurability() == info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ITEM_TYPE:
|
||
if( pItem->GetItemType() != info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ITEM_GROUP_NE:
|
||
if( pItem->GetItemGroup() == info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_AWAKEN_ITEM:
|
||
if( pItem->IsAwaken() == !info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_IDENTIFIED_ITEM:
|
||
if (pItem->IsIdentified() == !info.value[i])
|
||
return false;
|
||
break;
|
||
|
||
case MIX_CHECK_SAME_SUMMON_RATE:
|
||
{
|
||
#ifdef _DEBUG
|
||
// 소환수 카드가 아닌 아이템을 확인하는데 사용되어서는 안 되는 식별형태
|
||
// 만약 소환수 코드가 없거나 혹은 소환수 정보가 없는 경우는 아래의 GetSummonInfo를 얻어올 때 assert가 걸린다.
|
||
if( !pItem->IsSummonCard() )
|
||
{
|
||
assert( 0 );
|
||
return false;
|
||
}
|
||
#endif
|
||
if( !pItem->GetSummonCode() )
|
||
return false;
|
||
|
||
if( GameContent::GetSummonInfo( pItem->GetSummonCode() )->rate != info.value[i] )
|
||
return false;
|
||
}
|
||
break;
|
||
case MIX_CHECK_BASE_FLAG_ON:
|
||
if( !pItem->GetBaseFlag().IsOn( info.value[i] ) )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_BASE_FLAG_OFF:
|
||
if( pItem->GetBaseFlag().IsOn( info.value[i] ) )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_DESIGNER_DEFIEND_TYPE:
|
||
if( !check_designer_defined_type( pItem, info.value[i] ) )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ENHANCE_GE:
|
||
if( pItem->GetItemEnhance() < info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_ENHANCE_LE:
|
||
if( pItem->GetItemEnhance() > info.value[i] )
|
||
return false;
|
||
break;
|
||
case MIX_CHECK_SAME_ITEM_GROUP:
|
||
if( pItem->GetItemGroup() != info.value[i] )
|
||
return false;
|
||
break;
|
||
|
||
|
||
// Fraun Sky Accessories 7/16/2025
|
||
case MIX_CHECK_ADDITIONAL_ITEM_EFFECT: // 42
|
||
{
|
||
if (pItem->GetItemAdditionalEffect() != info.value[i]) return false;
|
||
}
|
||
break;
|
||
|
||
case MIX_CHECK_ADDITIONAL_ITEM_EFFECT_AWAKEN: // 43
|
||
{
|
||
if (info.value[i] != 10000 && info.value[i] != 1000) return false;
|
||
}
|
||
break;
|
||
|
||
case MIX_CHECK_ADDITIONAL_ITEM_EFFECT_ERASER: // 44
|
||
{
|
||
if (info.value[i] != 110000) return false;
|
||
}
|
||
break;
|
||
|
||
case MIX_CHECK_ITEM_GRADE_HIGHER: // 58
|
||
{
|
||
if (pItem->GetItemGrade() < info.value[i]) return false;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
if( !bIsCountChecked )
|
||
*pItemCount = 1;
|
||
|
||
return true;
|
||
}
|
||
|
||
const bool post_arrange_check_material_info( const MaterialInfo & info, const StructItem * pMainMaterial, int nSubMaterialCount, StructItem ** pArrangedSubMaterial, unsigned short *pArrangedCountList, const StructItem * pItem, unsigned short *pItemCount )
|
||
{
|
||
for( int i = 0 ; i < MaterialInfo::MATERIAL_INFO_COUNT ; ++i )
|
||
{
|
||
switch( info.type[ i ] )
|
||
{
|
||
case MIX_CHECK_SAME_ITEM_ID:
|
||
{
|
||
int nSlotIndex = info.value[ i ];
|
||
|
||
// 잘못된 비교 대상 슬롯 번호 데이터
|
||
if( 0 > nSlotIndex && nSlotIndex > nSubMaterialCount )
|
||
{
|
||
assert( 0 );
|
||
return false;
|
||
}
|
||
|
||
// 메인 슬롯의 아이템과 같은 아이템 코드
|
||
if( !nSlotIndex )
|
||
{
|
||
if( pItem->GetItemCode() != pMainMaterial->GetItemCode() )
|
||
return false;
|
||
}
|
||
else if( pItem->GetItemCode() != pArrangedSubMaterial[ nSlotIndex - 1 ]->GetItemCode() )
|
||
return false;
|
||
}
|
||
break;
|
||
case MIX_CHECK_SAME_SUMMON_CODE:
|
||
{
|
||
int nSlotIndex = info.value[ i ];
|
||
|
||
// 잘못된 비교 대상 슬롯 번호 데이터
|
||
if( 0 > nSlotIndex && nSlotIndex > nSubMaterialCount )
|
||
{
|
||
assert( 0 );
|
||
return false;
|
||
}
|
||
|
||
// 메인 슬롯의 아이템과 같은 소환수 코드 (ItemBase에 박혀 있는 소환수 코드를 우선으로 검사한다.)
|
||
int nItemSummonCode = (pItem->GetItemBase().nSummonId)? pItem->GetItemBase().nSummonId : pItem->GetSummonCode();
|
||
int nMaterialSummonCode = (pMainMaterial->GetItemBase().nSummonId)? pMainMaterial->GetItemBase().nSummonId : pMainMaterial->GetSummonCode();
|
||
|
||
if( nItemSummonCode != nMaterialSummonCode )
|
||
return false;
|
||
}
|
||
break;
|
||
case MIX_CHECK_SAME_ITEM_ENHANCE:
|
||
{
|
||
int nSlotIndex = info.value[ i ];
|
||
|
||
// 잘못된 비교 대상 슬롯 번호 데이터
|
||
if( 0 > nSlotIndex && nSlotIndex > nSubMaterialCount )
|
||
{
|
||
assert( 0 );
|
||
return false;
|
||
}
|
||
|
||
// 메인 슬롯의 아이템과 같은 강화 수치
|
||
if( !nSlotIndex )
|
||
{
|
||
if( pItem->GetItemEnhance() != pMainMaterial->GetItemEnhance() )
|
||
return false;
|
||
}
|
||
else if( pItem->GetItemEnhance() != pArrangedSubMaterial[ nSlotIndex - 1 ]->GetItemEnhance() )
|
||
return false;
|
||
}
|
||
break;
|
||
case MIX_CHECK_SAME_SKILL_ID:
|
||
{
|
||
int nSlotIndex = info.value[ i ];
|
||
|
||
// 잘못된 비교 대상 슬롯 번호 데이터
|
||
if( 0 > nSlotIndex && nSlotIndex > nSubMaterialCount )
|
||
{
|
||
assert( 0 );
|
||
return false;
|
||
}
|
||
|
||
// 메인 슬롯의 아이템과 같은 스킬 카드
|
||
if( !nSlotIndex )
|
||
{
|
||
if( pItem->GetSkillId() != pMainMaterial->GetSkillId() )
|
||
return false;
|
||
}
|
||
else if( pItem->GetSkillId() != pArrangedSubMaterial[ nSlotIndex - 1 ]->GetSkillId() )
|
||
return false;
|
||
}
|
||
break;
|
||
|
||
case MIX_CHECK_ITEM_FIRST_SOCKET_CODE_G:
|
||
{
|
||
int nSlotIndex = info.value[ i ];
|
||
|
||
// 잘못된 비교 대상 슬롯 번호 데이터
|
||
if( 0 > nSlotIndex && nSlotIndex > nSubMaterialCount )
|
||
{
|
||
assert( 0 );
|
||
return false;
|
||
}
|
||
|
||
// 메인 슬롯 아이템의 소켓 값보다 커야 한다.
|
||
if( !nSlotIndex )
|
||
{
|
||
if( pItem->GetSocketCode( 0 ) <= pMainMaterial->GetSocketCode( 0 ) )
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if( pItem->GetSocketCode( 0 ) <= pArrangedSubMaterial[ nSlotIndex - 1 ]->GetSocketCode( 0 ) )
|
||
return false;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case MIX_CHECK_ITEM_FIRST_SOCKET_CODE_L:
|
||
{
|
||
int nSlotIndex = info.value[ i ];
|
||
|
||
// 잘못된 비교 대상 슬롯 번호 데이터
|
||
if( 0 > nSlotIndex && nSlotIndex > nSubMaterialCount )
|
||
{
|
||
assert( 0 );
|
||
return false;
|
||
}
|
||
|
||
// 메인 슬롯 아이템의 소켓 값보다 작아야 한다.
|
||
if( !nSlotIndex )
|
||
{
|
||
if( pItem->GetSocketCode( 0 ) >= pMainMaterial->GetSocketCode( 0 ) )
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if( pItem->GetSocketCode( 0 ) >= pArrangedSubMaterial[ nSlotIndex - 1 ]->GetSocketCode( 0 ) )
|
||
return false;
|
||
}
|
||
}
|
||
break;
|
||
case MIX_CHECK_SAME_ITEM_CLASS:
|
||
{
|
||
int nSlotIndex = info.value[ i ];
|
||
|
||
// 잘못된 비교 대상 슬롯 번호 데이터
|
||
if( 0 > nSlotIndex && nSlotIndex > nSubMaterialCount )
|
||
{
|
||
assert( 0 );
|
||
return false;
|
||
}
|
||
|
||
// 메인 슬롯의 아이템과 같은 중분류
|
||
if( !nSlotIndex )
|
||
{
|
||
if( pItem->GetItemClass() != pMainMaterial->GetItemClass() )
|
||
return false;
|
||
}
|
||
else if( pItem->GetItemClass() != pArrangedSubMaterial[ nSlotIndex - 1 ]->GetItemClass() )
|
||
return false;
|
||
}
|
||
break;
|
||
case MIX_CHECK_INCLUDE_RACE_LIMIT:
|
||
{
|
||
int nSlotIndex = info.value[ i ];
|
||
|
||
// 잘못된 비교 대상 슬롯 번호 데이터
|
||
if( 0 > nSlotIndex && nSlotIndex > nSubMaterialCount )
|
||
{
|
||
assert( 0 );
|
||
return false;
|
||
}
|
||
|
||
const int RACE = ItemBase::LIMIT_DEVA | ItemBase::LIMIT_ASURA | ItemBase::LIMIT_GAIA;
|
||
|
||
// 메인 슬롯의 아이템과 같은 종족 제한
|
||
if( !nSlotIndex )
|
||
{
|
||
if( ( pItem->GetItemBase().nLimit & pMainMaterial->GetItemBase().nLimit & RACE ) != ( pItem->GetItemBase().nLimit & RACE ) )
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if( ( pItem->GetItemBase().nLimit & pArrangedSubMaterial[ nSlotIndex - 1 ]->GetItemBase().nLimit & RACE ) != ( pItem->GetItemBase().nLimit & RACE ) )
|
||
return false;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
const MixBase * MixManager::GetProperMixInfoAndArrangeSubMaterials( StructItem * pMainMaterial, int nSubMaterialCount, StructItem ** pSubMaterial, unsigned short *pCountList )
|
||
{
|
||
for( std::vector< MixBase >::iterator it = s_vMixInfo.begin(); it != s_vMixInfo.end(); ++it )
|
||
{
|
||
if( (*it).sub_material_cnt != nSubMaterialCount )
|
||
continue;
|
||
|
||
// 메인 슬롯은 그냥 무조건 1개인거다
|
||
unsigned short nMainMaterialCount = 1;
|
||
if( !check_material_info( (*it).main_material, pMainMaterial, &nMainMaterialCount ) )
|
||
continue;
|
||
|
||
bool bSuccess = true;
|
||
|
||
StructItem * apSubMaterialArrangeBuffer[ MixBase::MAX_SUB_MATERIAL_COUNT ] = { NULL };
|
||
unsigned short anSubMaterialArrangeCountList[ MixBase::MAX_SUB_MATERIAL_COUNT ] = { 0 };
|
||
bool abSubMaterialChecked[ MixBase::MAX_SUB_MATERIAL_COUNT ] = { false };
|
||
|
||
// 각 보조 재료 개별 조건 체크(루프 조건 체크에서 정확히는 nSubMaterialCount 대신 (*it).sub_material_cnt와 비교하는 게 맞지만 어차피 값은 같으므로 참조 단순화를 위해 nSubMaterialCount를 사용)
|
||
for( int nMaterialInfoIdx = 0 ; nMaterialInfoIdx < nSubMaterialCount ; ++nMaterialInfoIdx )
|
||
{
|
||
bool bFind = false;
|
||
|
||
for( int nSubMaterialIdx = 0 ; nSubMaterialIdx < nSubMaterialCount ; ++nSubMaterialIdx )
|
||
{
|
||
// 이미 체크되었던 아이템이면 패스
|
||
if( abSubMaterialChecked[ nSubMaterialIdx ] )
|
||
continue;
|
||
|
||
// 현재 재료 체크 조건에 만족되는 아이템이면 해당 아이템 정보를 세팅
|
||
if( check_material_info( (*it).sub_material[ nMaterialInfoIdx ], pSubMaterial[ nSubMaterialIdx ], pCountList + nSubMaterialIdx ) )
|
||
{
|
||
abSubMaterialChecked[ nSubMaterialIdx ] = true; // pSubMaterial[ nSubMaterialIdx ]는 체크 완료된 아이템이라는 플래그 체크
|
||
apSubMaterialArrangeBuffer[ nMaterialInfoIdx ] = pSubMaterial[ nSubMaterialIdx ]; // nMaterialInfoIdx 번째 보조 재료 조건에 해당된 아이템 포인터 보관
|
||
anSubMaterialArrangeCountList[ nMaterialInfoIdx ] = pCountList[ nSubMaterialIdx ]; // nMaterialInfoIdx 번째 보조 재료 조건에 해당된 아이템 개수 보관
|
||
|
||
bFind = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if( !bFind )
|
||
{
|
||
bSuccess = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if( !bSuccess )
|
||
continue;
|
||
|
||
// 보조 재료 개별 조건 체크에 만족된 경우 정렬된 SubMaterial 목록을 가지고 재료간 상호 연관 조건을 체크
|
||
if( !post_arrange_check_material_info( (*it).main_material, pMainMaterial, nSubMaterialCount,
|
||
apSubMaterialArrangeBuffer, anSubMaterialArrangeCountList, pMainMaterial, &nMainMaterialCount ) )
|
||
continue;
|
||
|
||
for( int nMaterialInfoIdx = 0 ; nMaterialInfoIdx < nSubMaterialCount ; ++nMaterialInfoIdx )
|
||
{
|
||
if( !post_arrange_check_material_info( (*it).sub_material[ nMaterialInfoIdx ], pMainMaterial, nSubMaterialCount, apSubMaterialArrangeBuffer,
|
||
anSubMaterialArrangeCountList, apSubMaterialArrangeBuffer[ nMaterialInfoIdx ], anSubMaterialArrangeCountList + nMaterialInfoIdx ) )
|
||
{
|
||
bSuccess = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if( !bSuccess )
|
||
continue;
|
||
|
||
// 재료간 상호 연관 조건 체크도 만족된 경우 정렬된 보조 재료 목록 및 개수 목록을 적용 후 조합 정보 리턴
|
||
s_memcpy( pSubMaterial, sizeof( StructItem * ) * nSubMaterialCount, apSubMaterialArrangeBuffer, sizeof( StructItem * ) * nSubMaterialCount );
|
||
s_memcpy( pCountList, sizeof( *pCountList ) * nSubMaterialCount, anSubMaterialArrangeCountList, sizeof( unsigned short ) * nSubMaterialCount );
|
||
return &(*it);
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
bool MixManager::FindAndDecompose( struct StructPlayer* pPlayer, std::vector< std::pair< struct StructItem*, __int64 > >* vCreatedItem, AR_HANDLE hItem, int nCount )
|
||
{
|
||
StructItem::iterator it = StructItem::get( hItem );
|
||
if( !(*it) ) return false;
|
||
|
||
StructItem *pItem = static_cast< StructItem * >( *it );
|
||
if( !pItem || !pItem->IsItem() || nCount > pItem->GetCount() || nCount > GameRule::MAX_DECOMPOSE_COUNT_PER_ITEM )
|
||
{
|
||
return false;
|
||
}
|
||
|
||
if( !pPlayer->IsDecomposable( pItem ) )
|
||
{
|
||
return false;
|
||
}
|
||
|
||
for( std::vector< DecomposeBase >::iterator it = s_vDecomposeInfo.begin(); it != s_vDecomposeInfo.end(); ++it )
|
||
{
|
||
unsigned short nTemporaryCount = 1;
|
||
if( check_material_info( it->material, pItem, &nTemporaryCount ) )
|
||
{
|
||
int cost = it->neededPrice * nCount;
|
||
// 조합에 필요한 돈 계산
|
||
if( cost > pPlayer->GetGold().GetRawData() )
|
||
{
|
||
return false;
|
||
}
|
||
|
||
pPlayer->ChangeGold( pPlayer->GetGold() - cost );
|
||
|
||
// 아이템 지우기
|
||
LOG::Log11N4S( LM_ITEM_DECOMPOSE_MATERIAL, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemCode(), nCount, pItem->GetItemEnhance() * 100, pItem->GetItemLevel(), cost, pPlayer->GetGold().GetRawData(), 0, DecomposeBase::DECOMPOSE_CREATE_ITEM, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "DECOMPOSE_CREATE_ITEM", LOG::STR_NTS );
|
||
if( !pPlayer->EraseItem( pItem, nCount ) )
|
||
{
|
||
pPlayer->ChangeGold( pPlayer->GetGold() + cost ); // 삭제된 돈 복구해준다.
|
||
return false;
|
||
}
|
||
|
||
switch( it->type )
|
||
{
|
||
case DecomposeBase::DECOMPOSE_CREATE_ITEM:
|
||
if( !MixManager::CreateItemByDecompose( pPlayer, (*it), vCreatedItem, nCount ) )
|
||
return false;
|
||
break;
|
||
|
||
default:
|
||
return false;
|
||
|
||
}
|
||
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
void MixManager::RegisterEnhanceInfo( EnhanceInfo & info )
|
||
{
|
||
std::vector< EnhanceInfo >::iterator it;
|
||
for( it = s_vEnhanceInfo.begin(); it != s_vEnhanceInfo.end(); ++it )
|
||
{
|
||
if( (*it).nSID == info.nSID )
|
||
{
|
||
(*it) = info;
|
||
return;
|
||
}
|
||
}
|
||
|
||
s_vEnhanceInfo.push_back( info );
|
||
}
|
||
|
||
const EnhanceInfo * getEnhanceInfo( int sid )
|
||
{
|
||
std::vector< EnhanceInfo >::iterator it;
|
||
for( it = s_vEnhanceInfo.begin(); it != s_vEnhanceInfo.end(); ++it )
|
||
{
|
||
if( (*it).nSID == sid )
|
||
return &(*it);
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
bool MixManager::RestoreEnhance( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
int nCurrentEnhance = pMainMaterial->GetItemEnhance();
|
||
|
||
StructItem * pPowder = pSubMaterial[0];
|
||
|
||
if( !pPowder )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 로그
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pPowder->GetItemEnhance() * 100 + pPowder->GetItemLevel(), pPowder->GetItemCode(), 1, pPowder->GetCount() - 1, pMixInfo->id, pPowder->GetCurrentEtherealDurability(), 0, 0, pPowder->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "EITEM", LOG::STR_NTS );
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "EITEM", LOG::STR_NTS );
|
||
|
||
pPlayer->EraseItem( pPowder, 1 );
|
||
|
||
ItemUID uid = pMainMaterial->GetItemUID();
|
||
|
||
switch( ENV().GetInt( "game.enhance_fail_type", 0 ) )
|
||
{
|
||
case 1:
|
||
nCurrentEnhance = nCurrentEnhance - 1;
|
||
break;
|
||
case 2:
|
||
// Proceed with the enhancement values as-is
|
||
// nCurrentEnhance = nCurrentEnhance;
|
||
break;
|
||
case 0:
|
||
default:
|
||
nCurrentEnhance = std::max(nCurrentEnhance - (20 - nCurrentEnhance) * (pMixInfo->value[3] - XRandom(pMixInfo->value[2], pMixInfo->value[3])) / (pMixInfo->value[3] - pMixInfo->value[2] + 1), 0); // Fraun formula change to more realistic values 8/18/2025
|
||
//nCurrentEnhance = ( ( XRandom( nCurrentEnhance * pMixInfo->value[2], nCurrentEnhance * pMixInfo->value[3] ) / 1000 ) + 5 ) / 10;
|
||
break;
|
||
}
|
||
|
||
// 성공
|
||
pMainMaterial->SetItemEnhance( nCurrentEnhance );
|
||
|
||
if( pMixInfo->value[1] )
|
||
pMainMaterial->SetInstanceFlagOn( pMixInfo->value[0] );
|
||
else
|
||
pMainMaterial->SetInstanceFlagOff( pMixInfo->value[0] );
|
||
|
||
if( pPowder && pPowder->GetItemGrade() > 0 )
|
||
{
|
||
int min = std::max( ENV().GetInt( "game.enhance_chance_min", 0 ), 1 );
|
||
int max = std::min( ENV().GetInt( "game.enhance_chance_max", 0 ), 50 );
|
||
|
||
int chance = pPowder->GetItemGrade() * XRandom( min, max );
|
||
if( chance > 0 )
|
||
{
|
||
std::string strChance;
|
||
|
||
pMainMaterial->SetItemEnhanceChance(
|
||
std::min ( chance + pMainMaterial->GetItemEnhanceChance(), 999 ) );
|
||
|
||
XStringUtil::Format( strChance, "%.1f", chance / 10.0f );
|
||
PrintfChatMessage( false, CHAT_NOTICE, "@SYSTEM", pPlayer, "@1584\v#@value@#\v+%s", strChance.c_str() );
|
||
}
|
||
}
|
||
|
||
// DB 업데이트, SendItemMessage는 이하에서 담당
|
||
pMainMaterial = pPlayer->onAfterChangeItemProperty( pMainMaterial );
|
||
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
bool MixManager::EnhanceItem( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
int nCurrentEnhance = pMainMaterial->GetItemEnhance();
|
||
|
||
const EnhanceInfo *pInfo = getEnhanceInfo( pMixInfo->value[0] );
|
||
|
||
// 강화정보가 없음
|
||
if( !pInfo )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 정렬되어 들어오기 때문에 첫 슬롯은 항상 큐브
|
||
StructItem * pCube = pSubMaterial[0];
|
||
if( !pCube || pCube->GetItemCode() != pInfo->nNeedItemCode )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 강화 수치 결정
|
||
int itemEnhance = ( pMixInfo->type == MixBase::MIX_ENHANCE ) ? XRandom( pMixInfo->value[1], pMixInfo->value[2] ) : 1;
|
||
|
||
if( pInfo->nMaxEnhance < pMainMaterial->GetItemEnhance() + itemEnhance )
|
||
itemEnhance = pInfo->nMaxEnhance - pMainMaterial->GetItemEnhance();
|
||
|
||
// 강화 max치 검사
|
||
if( itemEnhance <= 0 )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 로그
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "EITEM", LOG::STR_NTS );
|
||
for( int i = 0; i < nSubMaterialCount; ++i )
|
||
{
|
||
StructItem *pItem = pSubMaterial[i];
|
||
unsigned short nCount = pCountList[i];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), nCount, pItem->GetCount() - nCount, pMixInfo->id, pCube->GetCurrentEtherealDurability(), 0, 0, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "EITEM", LOG::STR_NTS );
|
||
|
||
pPlayer->EraseItem( pItem, nCount );
|
||
}
|
||
|
||
ItemUID uid = pMainMaterial->GetItemUID();
|
||
|
||
int nBonus = (int)(pMainMaterial->GetItemEnhanceChance() * 100);
|
||
int nRandom = (int)(pInfo->fPercentage[ nCurrentEnhance ] * 100000);
|
||
|
||
bool bResult = false;
|
||
const char * szRes = "FAIL";
|
||
|
||
if( XRandom( 0, 100000 ) <= std::min( nRandom + nBonus, 100000 ) )
|
||
{
|
||
// 성공
|
||
pMainMaterial->SetItemEnhance( nCurrentEnhance + itemEnhance );
|
||
// DB 업데이트, SendItemMessage는 이하에서 담당
|
||
pMainMaterial = pPlayer->onAfterChangeItemProperty( pMainMaterial );
|
||
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
PrintfChatMessage(false, CHAT_ITEM, "@SYSTEM", pPlayer, "@9715");
|
||
|
||
|
||
szRes = "SUCS";
|
||
bResult = true;
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS );
|
||
}
|
||
else
|
||
{
|
||
// 실패
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS );
|
||
|
||
//AziaMafia if( pMixInfo->type != MixBase::MIX_ENHANCE_WITHOUT_FAIL )
|
||
if (pMixInfo->type != MixBase::MIX_ENHANCE_WITHOUT_FAIL && ENV().GetInt("game.enchance_no_fail", 0) == 0)
|
||
{
|
||
// No need to handle DB update here – it's already done in procEnhanceFail()
|
||
MixManager::procEnhanceFail( pPlayer, pMainMaterial, pInfo->nFailResult );
|
||
}
|
||
else
|
||
{
|
||
switch( ENV().GetInt( "game.enhance_fail_type", 0 ) )
|
||
{
|
||
case 1:
|
||
if (rand() % 100 + 1 <= ENV().GetInt("game.enhance_fail_type_chance", 50 ) )
|
||
pMainMaterial->SetItemEnhance(std::max((nCurrentEnhance - itemEnhance ) , 0) );
|
||
case 2:
|
||
// Proceed with the current enhancement value as is
|
||
// pMainMaterial->SetItemEnhance( nCurrentEnhance );
|
||
break;
|
||
case 0:
|
||
default:
|
||
pMainMaterial->SetItemEnhance( ( ( XRandom( nCurrentEnhance * pMixInfo->value[2], nCurrentEnhance * pMixInfo->value[3] ) / 1000 ) + 5 ) / 10 );
|
||
break;
|
||
}
|
||
|
||
StructItem* pPowder = pSubMaterial[1];
|
||
if( ENV().GetInt( "game.enhance_chance", 0 ) && pPowder && pPowder->GetItemGrade() > 0 )
|
||
{
|
||
int min = std::max( ENV().GetInt( "game.enhance_chance_min", 0 ), 1 );
|
||
int max = std::min( ENV().GetInt( "game.enhance_chance_max", 0 ), 50 );
|
||
|
||
int chance = pPowder->GetItemGrade() * XRandom( min, max );
|
||
if( chance > 0 )
|
||
{
|
||
std::string strChance;
|
||
|
||
pMainMaterial->SetItemEnhanceChance(
|
||
std::min( chance + pMainMaterial->GetItemEnhanceChance(), 999 ) );
|
||
|
||
XStringUtil::Format( strChance, "%.1f", chance / 10.0f );
|
||
PrintfChatMessage( false, CHAT_NOTICE, "@SYSTEM", pPlayer, "@1584\v#@value@#\v+%s", strChance.c_str() );
|
||
}
|
||
}
|
||
|
||
// DB 업데이트, SendItemMessage는 이하에서 담당
|
||
pMainMaterial = pPlayer->onAfterChangeItemProperty( pMainMaterial );
|
||
}
|
||
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult(pPlayer, &tmp, 1);
|
||
PrintfChatMessage(false, CHAT_ITEM, "@SYSTEM", pPlayer, "@9716");
|
||
//SendMixResult( pPlayer, NULL, 0 );
|
||
}
|
||
|
||
return bResult;
|
||
}
|
||
|
||
bool MixManager::EnhanceSkillCard( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
const EnhanceInfo *pInfo = getEnhanceInfo( pMixInfo->value[0] );
|
||
|
||
// 강화정보가 없음
|
||
if( !pInfo )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 정렬되어 들어오기 때문에 첫 슬롯은 항상 큐브
|
||
StructItem * pCube = pSubMaterial[0];
|
||
if( !pCube || pCube->GetItemCode() != pInfo->nNeedItemCode )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 강화 MAX치 검사
|
||
if( pInfo->nMaxEnhance <= pMainMaterial->GetItemEnhance() )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 카드가 중첩되면서 메인 슬롯과 보조 슬롯이 같을 수 있다 이 경우 최소 2개 이상인지 체크 (메인 슬롯 = 보조 슬롯이 되버림)
|
||
if( ( pMainMaterial == pSubMaterial[1] ) && pMainMaterial->GetCount() < 2 )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 보조재료 날려주고
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "ESKILLCARD", LOG::STR_NTS );
|
||
for( int i = 0; i < nSubMaterialCount; ++i )
|
||
{
|
||
StructItem *pItem = pSubMaterial[i];
|
||
unsigned short nCount = pCountList[i];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), nCount, pItem->GetCount() - nCount, pMixInfo->id, pCube->GetCurrentEtherealDurability(), 0, 0, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "ESKILLCARD", LOG::STR_NTS );
|
||
|
||
pPlayer->EraseItem( pItem, nCount );
|
||
}
|
||
|
||
ItemUID uid = pMainMaterial->GetItemUID();
|
||
int nRandom = (int)( pInfo->fPercentage[ pMainMaterial->GetItemEnhance() ] * 100000 );
|
||
|
||
bool bResult = false;
|
||
const char * szRes = "FAIL";
|
||
|
||
if( XRandom( 0, 100000 ) <= nRandom )
|
||
{
|
||
// 성공했으므로 기존 카드 제거 후 +1짜리 하나 생성 해서 하나 인벤에 넣어준다.
|
||
// 우선 +1강 카드 생성
|
||
StructItem * pNewItem = StructItem::AllocItem( 0, pMainMaterial->GetItemCode(), 1 );
|
||
pNewItem->CopyFrom( pMainMaterial );
|
||
pNewItem->SetItemEnhance( pNewItem->GetItemEnhance() + 1 );
|
||
pNewItem->SetIdx( 0 );
|
||
pNewItem->SetCount( 1 );
|
||
pPlayer->PushItem( pNewItem, 1 );
|
||
// 그리고 기존 메인 슬롯의 카드 1개 제거
|
||
pPlayer->EraseItem( pMainMaterial, 1 );
|
||
|
||
AR_HANDLE tmp = pNewItem->GetHandle();
|
||
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
|
||
bResult = true;
|
||
szRes = "SUCS";
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pNewItem->GetItemEnhance() * 100 + pNewItem->GetItemLevel(), pNewItem->GetItemCode(), 1, pMixInfo->id, pNewItem->GetCurrentEtherealDurability(), 0, 0, 0, pNewItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS );
|
||
}
|
||
else
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS );
|
||
|
||
// 실패처리. 만약 MIX_ENHANCE_SKILL_CARD_WITHOUT_FAIL 타입이면 메인 카드는 살려준다.
|
||
if( pMixInfo->type != MixBase::MIX_ENHANCE_SKILL_CARD_WITHOUT_FAIL )
|
||
{
|
||
// procEnhanceFail 함수 안에서 DB Update를 처리하므로 아래에서 따로 처리할 필요 없음
|
||
MixManager::procEnhanceFail( pPlayer, pMainMaterial, pInfo->nFailResult );
|
||
}
|
||
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
}
|
||
|
||
return bResult;
|
||
}
|
||
|
||
bool MixManager::EnhanceCreatureCard( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
struct StructItem *pMainCreatureCard = pMainMaterial;
|
||
struct StructItem *pSubCreatureCard = pSubMaterial[ 0 ];
|
||
struct StructItem *pSoulMixer = pSubMaterial[ 1 ];
|
||
struct StructItem *pJoker = NULL;
|
||
if( pMixInfo->type == MixBase::MIX_ENHANCE_CREATURE_CARD_WITH_JOKER )
|
||
pJoker = pSubMaterial[ 2 ];
|
||
|
||
// 조커 카드는 예외적으로 강화가 제한된다.
|
||
if( pMainCreatureCard->GetItemCode() == ItemBase::ITEM_CODE_JOKER )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 내구도가 다한 카드는 조합할 수 없다.
|
||
if( ( pMainCreatureCard->GetMaxEtherealDurability() && !pMainCreatureCard->GetCurrentEtherealDurability() ) || ( pSubCreatureCard->GetMaxEtherealDurability() && !pSubCreatureCard->GetCurrentEtherealDurability() ) )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 두 크리처카드는 강화 단계가 같아야 함.
|
||
if( pMainCreatureCard->GetItemEnhance() != pSubCreatureCard->GetItemEnhance() )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
const EnhanceInfo *pInfo = getEnhanceInfo( pMixInfo->value[0] );
|
||
|
||
// 강화정보가 없음
|
||
if( !pInfo )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 강화 max치 검사
|
||
if( pInfo->nMaxEnhance <= pMainCreatureCard->GetItemEnhance() )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 편성되어 있으면 실패
|
||
for( int nBindIndex = 0 ; nBindIndex < 6 ; ++nBindIndex )
|
||
{
|
||
StructItem *pItem = pPlayer->GetSummonCardAt( nBindIndex );
|
||
|
||
if( pItem == pMainCreatureCard || pItem == pSubCreatureCard || ( pJoker && pItem == pJoker ) )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// AziaMafia Stage Pet Rework
|
||
// Determination of probability: base enhancement probability + sum of creature levels
|
||
// 8 + success rate of Joker cards
|
||
int nMainLevel = 1 ; // pMainCreatureCard->GetSummonStruct() ? pMainCreatureCard->GetSummonStruct()->GetLevel() : 1;
|
||
int nSubLevel = 1 ; // pSubCreatureCard->GetSummonStruct() ? pSubCreatureCard->GetSummonStruct()->GetLevel() : 1;
|
||
|
||
int nRate = static_cast< int >( pInfo->fPercentage[ pMainCreatureCard->GetItemEnhance() ] * 100000 );
|
||
int nAdditionRate = ( nMainLevel + nSubLevel ) * 125;
|
||
if( pJoker && pJoker->GetSummonStruct() )
|
||
{
|
||
StructSkill *pSkill = pJoker->GetSummonStruct()->GetCurrentPassiveSkill( StructSkill::SKILL_FRIENDSHIP_OF_CROWN );
|
||
if( pSkill ) nAdditionRate += pSkill->GetVar( 0 ) * pSkill->GetCurrentSkillLevel() * 1000;
|
||
|
||
}
|
||
|
||
// 로그
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainCreatureCard->GetItemEnhance() * 100 + pMainCreatureCard->GetItemLevel(), pMainCreatureCard->GetItemCode(), 1, pMainCreatureCard->GetCount() - 1, pMixInfo->id, 0, 0, 0, pMainCreatureCard->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "MIX_ENHANCE_CREATURE_CARD", LOG::STR_NTS );
|
||
for( int i = 0; i < nSubMaterialCount; ++i )
|
||
{
|
||
StructItem *pItem = pSubMaterial[i];
|
||
unsigned short nCount = pCountList[i];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), nCount, pItem->GetCount() - nCount, pMixInfo->id, 0, 0, 0, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "MIX_ENHANCE_CREATURE_CARD", LOG::STR_NTS );
|
||
|
||
pPlayer->EraseItem( pItem, nCount );
|
||
}
|
||
|
||
bool bResult = false;
|
||
const char * szRes = "FAIL";
|
||
|
||
// Fraun 8/18/2025 joker always causes success on enhance
|
||
bool bJokerAlwaysSuccess = (ENV().GetInt( "game.joker_always_success", 0) == 1);
|
||
|
||
if( ( XRandom( 0, 100000 ) <= nRate + nAdditionRate ) || bJokerAlwaysSuccess)
|
||
{
|
||
// 강화 수치에 맞는 카드 내구도 세팅
|
||
pMainCreatureCard->SetItemEnhance( pMainCreatureCard->GetItemEnhance() + 1 );
|
||
pMainCreatureCard->SetCurrentEtherealDurability( pMainCreatureCard->GetMaxEtherealDurability() );
|
||
|
||
if( pMainCreatureCard->GetSummonStruct() )
|
||
{
|
||
StructSummon *pSummon = pMainCreatureCard->GetSummonStruct();
|
||
|
||
pSummon->Reborn();
|
||
pSummon->DBQuery( new DB_UpdateSummon( pSummon ) );
|
||
}
|
||
|
||
// DB 업데이트, SendItemMessage는 이하에서 담당
|
||
pMainCreatureCard = pPlayer->onAfterChangeItemProperty( pMainCreatureCard );
|
||
|
||
AR_HANDLE tmp = pMainCreatureCard->GetHandle();
|
||
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
|
||
bResult = true;
|
||
szRes = "SUCS";
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainCreatureCard->GetItemEnhance() * 100 + pMainCreatureCard->GetItemLevel(), pMainCreatureCard->GetItemCode(), 1, pMixInfo->id, pMainCreatureCard->GetCurrentEtherealDurability(), 0, 0, 0, pMainCreatureCard->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS );
|
||
|
||
pPlayer->UpdateTitleConditionBySummonEnhance( pMainCreatureCard->GetSummonCode(), GameContent::GetSummonInfo( pMainCreatureCard->GetSummonCode() )->rate, pMainCreatureCard->GetItemEnhance() );
|
||
}
|
||
else
|
||
{
|
||
// Fraun 8/18/2025 always get joker on failure
|
||
bool bAlwaysGetJokerOnFail = (ENV().GetInt("game.always_get_joker", 0) == 1);
|
||
|
||
// Enhancement cards of +3 or higher / Normal Basic or higher
|
||
if( pMainCreatureCard->GetItemEnhance() >= 1 && // 3
|
||
GameContent::GetSummonInfo( pMainCreatureCard->GetSummonCode() )->rate >= 2 && // 1
|
||
(int)XRandom( 0, 100 ) <= pMixInfo->value[5] || bAlwaysGetJokerOnFail )
|
||
{
|
||
// Item Assignment
|
||
ItemBase::ItemCode nItemID = pMixInfo->value[3];
|
||
__int64 nItemCount = 1;
|
||
|
||
while( nItemID < 0 )
|
||
{
|
||
GameContent::SelectItemIDFromDropGroup( nItemID, nItemID, nItemCount );
|
||
}
|
||
|
||
if( nItemID > 0 )
|
||
{
|
||
StructItem *pItem = StructItem::AllocItem( 0, nItemID, nItemCount, ItemInstance::BY_MIX, -1 );
|
||
|
||
if( pItem->IsJoinable() )
|
||
{
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@254\v#@item_name@#\v@%d\v#@item_num@#\v%d", pItem->GetItemBase().nNameId, pItem->GetCount() );
|
||
}
|
||
else
|
||
{
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@253\v#@item_name@#\v@%d", pItem->GetItemBase().nNameId );
|
||
}
|
||
|
||
StructItem *pNewItem = pPlayer->PushItem( pItem, pItem->GetCount() );
|
||
if( pNewItem )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_TAKE, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), pItem->GetCount(), pNewItem->GetCount(), pPlayer->GetGold().GetRawData(), pPlayer->GetGold().GetRawData(), pPlayer->GetX(), pPlayer->GetY(), 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "MIX", LOG::STR_NTS );
|
||
}
|
||
|
||
if( pNewItem != pItem )
|
||
{
|
||
StructItem::PendFreeItem( pItem );
|
||
}
|
||
}
|
||
}
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainCreatureCard->GetItemEnhance() * 100 + pMainCreatureCard->GetItemLevel(), pMainCreatureCard->GetItemCode(), 1, pMixInfo->id, pMainCreatureCard->GetCurrentEtherealDurability(), 0, 0, 0, pMainCreatureCard->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS );
|
||
|
||
// Since the DB update is handled inside the procEnhanceFail function, there’s no need to handle it separately below
|
||
MixManager::procEnhanceFail( pPlayer, pMainCreatureCard, pInfo->nFailResult );
|
||
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
}
|
||
|
||
return bResult;
|
||
}
|
||
|
||
bool MixManager::UltimateEnhance( const MixBase * pMixInfo, StructPlayer * pPlayer, StructItem * pMainMaterial, int nSubMaterialCount, StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
// 유형 111: 궁극 강화
|
||
// 값0: 강화ID, 값1: 궁극강화를하기위한최소강화수치, 값2/값3: 강화 수치 최소/최대
|
||
|
||
const EnhanceInfo *pInfo = getEnhanceInfo( pMixInfo->value[0] );
|
||
// 강화정보가 없음
|
||
if( !pInfo )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
int nCurrentEnhance = pMainMaterial->GetItemEnhance();
|
||
// 최소 강화 수치 만족하나 검사
|
||
if( pMixInfo->value[1] > nCurrentEnhance )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 기본적으로 궁극 강화를 하려면 정상적인 아이템이어야한다. 유니드카드화/강화실패작/내구0짜리 안됨
|
||
// AziaMafia Fix COMBI CARTE PETS
|
||
//if( pMainMaterial->GetInstanceFlag().IsOn( ItemInstance::ITEM_FLAG_CARD ) ||
|
||
if(
|
||
pMainMaterial->GetInstanceFlag().IsOn( ItemInstance::ITEM_FLAG_FAILED ) ||
|
||
( pMainMaterial->GetMaxEtherealDurability() && !pMainMaterial->GetCurrentEtherealDurability() ) )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 첫 슬롯은 항상 강화 촉매 아이템
|
||
StructItem * pMaterial = pSubMaterial[0];
|
||
if( !pMaterial || pMaterial->GetItemCode() != pInfo->nNeedItemCode )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 강화 수치 결정
|
||
int itemEnhance = XRandom( pMixInfo->value[2], pMixInfo->value[3] );
|
||
|
||
if( pInfo->nMaxEnhance < pMainMaterial->GetItemEnhance() + itemEnhance )
|
||
itemEnhance = pInfo->nMaxEnhance - pMainMaterial->GetItemEnhance();
|
||
|
||
// 강화 max치 검사
|
||
if( itemEnhance <= 0 )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 로그
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "ULTI_ENHANCE", LOG::STR_NTS );
|
||
for( int i = 0; i < nSubMaterialCount; ++i )
|
||
{
|
||
StructItem *pItem = pSubMaterial[i];
|
||
unsigned short nCount = pCountList[i];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), nCount, pItem->GetCount() - nCount, pMixInfo->id, pItem->GetCurrentEtherealDurability(), 0, 0, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "ULTI_ENHANCE", LOG::STR_NTS );
|
||
|
||
pPlayer->EraseItem( pItem, nCount );
|
||
}
|
||
|
||
ItemUID uid = pMainMaterial->GetItemUID();
|
||
|
||
int nRandom = (int)(pInfo->fPercentage[ nCurrentEnhance ] * 100000);
|
||
|
||
bool bResult = false;
|
||
const char * szRes = "FAIL";
|
||
|
||
if( XRandom( 0, 100000 ) <= nRandom )
|
||
{
|
||
// 성공
|
||
pMainMaterial->SetItemEnhance( nCurrentEnhance + itemEnhance );
|
||
// DB 업데이트, SendItemMessage는 이하에서 담당
|
||
pMainMaterial = pPlayer->onAfterChangeItemProperty( pMainMaterial );
|
||
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
|
||
szRes = "SUCS";
|
||
bResult = true;
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS );
|
||
}
|
||
else
|
||
{
|
||
// 실패
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS );
|
||
|
||
// procEnhanceFail 함수 안에서 DB Update를 처리하므로 따로 처리할 필요 없음
|
||
MixManager::procEnhanceFail( pPlayer, pMainMaterial, pInfo->nFailResult );
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
}
|
||
|
||
return bResult;
|
||
}
|
||
|
||
bool MixManager::MixItemLevel( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
int nEnhance = 0;
|
||
int nLevel = 0;
|
||
|
||
if( pMixInfo->type == MixBase::MIX_SET_LEVEL ||
|
||
pMixInfo->type == MixBase::MIX_SET_LEVEL_WITH_FAIL ||
|
||
pMixInfo->type == MixBase::MIX_SET_LEVEL_CREATE_ITEM ||
|
||
pMixInfo->type == MixBase::MIX_SET_LEVEL_SET_FLAG ||
|
||
pMixInfo->type == MixBase::MIX_SET_LEVEL_SET_FLAG_CREATE_ITEM ||
|
||
pMixInfo->type == MixBase::MIX_SET_LEVEL_SET_FLAG_CREATE_ITEM_WITH_MAIN_MATERIAL_LEVEL )
|
||
{
|
||
nEnhance = pMixInfo->value[0] / 100;
|
||
if( !nEnhance && pMainMaterial ) nEnhance = pMainMaterial->GetItemEnhance();
|
||
nLevel = pMixInfo->value[0] % 100;
|
||
if( !nLevel && pMainMaterial ) nLevel = pMainMaterial->GetItemLevel();
|
||
}
|
||
else if( pMixInfo->type == MixBase::MIX_SET_LEVEL_SET_FLAG_CREATE_ITEM_WITH_MAIN_MATERIAL_LEVEL_SET_ZERO )
|
||
{
|
||
nEnhance = pMixInfo->value[0] / 100;
|
||
nLevel = pMixInfo->value[0] % 100;
|
||
}
|
||
else if( pMixInfo->type == MixBase::MIX_SET_LEVEL_ON_SUB_MATERIAL_LEVEL_SET_FLAG )
|
||
{
|
||
int i = pMixInfo->value[0];
|
||
assert( i >= 1 && i <= nSubMaterialCount );
|
||
|
||
nEnhance = pSubMaterial[i-1]->GetItemEnhance();
|
||
nLevel = pSubMaterial[i-1]->GetItemLevel();
|
||
}
|
||
else if( pMixInfo->type == MixBase::MIX_ADD_LEVEL ||
|
||
pMixInfo->type == MixBase::MIX_ADD_LEVEL_CREATE_ITEM ||
|
||
pMixInfo->type == MixBase::MIX_ADD_LEVEL_SET_FLAG ||
|
||
pMixInfo->type == MixBase::MIX_ADD_LEVEL_SET_FLAG_CREATE_ITEM )
|
||
{
|
||
nEnhance = pMainMaterial->GetItemEnhance() + pMixInfo->value[0] / 100;
|
||
nLevel = pMainMaterial->GetItemLevel() + pMixInfo->value[0] % 100;
|
||
}
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial ? pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel() : 0, pMainMaterial ? pMainMaterial->GetItemCode() : 0, 1, pMainMaterial ? pMainMaterial->GetCount() : 0, pMixInfo->id, pMainMaterial ? pMainMaterial->GetCurrentEtherealDurability() : 0, 0, 0, pMainMaterial ? pMainMaterial->GetItemUID() : 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "ITEMLVUP", LOG::STR_NTS );
|
||
|
||
for( int i = 0; i < nSubMaterialCount; ++i )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pSubMaterial[i]->GetItemEnhance() * 100 + pSubMaterial[i]->GetItemLevel(), pSubMaterial[i]->GetItemCode(), 1, pSubMaterial[i]->GetCount() - 1, pMixInfo->id, pSubMaterial[i]->GetCurrentEtherealDurability(), 0, 0, pSubMaterial[i]->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "ITEMLVUP", LOG::STR_NTS );
|
||
pPlayer->EraseItem( pSubMaterial[i], pCountList[i] );
|
||
}
|
||
|
||
bool bSuccess = true;
|
||
|
||
StructItem * pNewItem = NULL;
|
||
|
||
if( pMixInfo->type == MixBase::MIX_SET_LEVEL_WITH_FAIL )
|
||
{
|
||
if( (int)XRandom( 0, 100 ) > pMixInfo->value[1] )
|
||
bSuccess = false;
|
||
}
|
||
|
||
if( pMixInfo->type == MixBase::MIX_SET_LEVEL_CREATE_ITEM ||
|
||
pMixInfo->type == MixBase::MIX_SET_LEVEL_SET_FLAG_CREATE_ITEM ||
|
||
pMixInfo->type == MixBase::MIX_SET_LEVEL_SET_FLAG_CREATE_ITEM_WITH_MAIN_MATERIAL_LEVEL ||
|
||
pMixInfo->type == MixBase::MIX_SET_LEVEL_SET_FLAG_CREATE_ITEM_WITH_MAIN_MATERIAL_LEVEL_SET_ZERO ||
|
||
pMixInfo->type == MixBase::MIX_ADD_LEVEL_CREATE_ITEM ||
|
||
pMixInfo->type == MixBase::MIX_ADD_LEVEL_SET_FLAG_CREATE_ITEM )
|
||
{
|
||
if( (int)XRandom( 0, 100 ) <= pMixInfo->value[5] )
|
||
{
|
||
// 아이템 할당
|
||
ItemBase::ItemCode nItemID = pMixInfo->value[3];
|
||
__int64 nItemCount = 1;
|
||
|
||
if( nItemID < 0 )
|
||
{
|
||
GameContent::SelectItemIDFromDropGroup( nItemID, nItemID, nItemCount );
|
||
}
|
||
|
||
if( nItemID > 0 )
|
||
{
|
||
int nEnhance = pMixInfo->value[4] / 100;
|
||
int nLevel = pMixInfo->value[4] % 100;
|
||
|
||
if( pMixInfo->type == MixBase::MIX_SET_LEVEL_SET_FLAG_CREATE_ITEM_WITH_MAIN_MATERIAL_LEVEL ||
|
||
pMixInfo->type == MixBase::MIX_SET_LEVEL_SET_FLAG_CREATE_ITEM_WITH_MAIN_MATERIAL_LEVEL_SET_ZERO )
|
||
{
|
||
nEnhance = pMainMaterial->GetItemEnhance();
|
||
nLevel = pMainMaterial->GetItemLevel();
|
||
}
|
||
|
||
StructItem *pItem = StructItem::AllocItem( 0, nItemID, nItemCount, ItemInstance::BY_MIX, nLevel, nEnhance );
|
||
|
||
if( pItem->IsJoinable() )
|
||
{
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@254\v#@item_name@#\v@%d\v#@item_num@#\v%d", pItem->GetItemBase().nNameId, pItem->GetCount() );
|
||
}
|
||
else
|
||
{
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@253\v#@item_name@#\v@%d", pItem->GetItemBase().nNameId );
|
||
}
|
||
|
||
pNewItem = pPlayer->PushItem( pItem, pItem->GetCount() );
|
||
if( pNewItem )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_TAKE, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), pItem->GetCount(), pNewItem->GetCount(), pPlayer->GetGold().GetRawData(), pPlayer->GetGold().GetRawData(), pPlayer->GetX(), pPlayer->GetY(), 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "MIX", LOG::STR_NTS );
|
||
}
|
||
|
||
if( pNewItem != pItem )
|
||
{
|
||
StructItem::PendFreeItem( pItem );
|
||
}
|
||
}
|
||
}
|
||
else
|
||
bSuccess = false;
|
||
}
|
||
|
||
if( pMixInfo->type == MixBase::MIX_SET_LEVEL_SET_FLAG ||
|
||
pMixInfo->type == MixBase::MIX_SET_LEVEL_SET_FLAG_CREATE_ITEM ||
|
||
pMixInfo->type == MixBase::MIX_SET_LEVEL_SET_FLAG_CREATE_ITEM_WITH_MAIN_MATERIAL_LEVEL ||
|
||
pMixInfo->type == MixBase::MIX_SET_LEVEL_SET_FLAG_CREATE_ITEM_WITH_MAIN_MATERIAL_LEVEL_SET_ZERO ||
|
||
pMixInfo->type == MixBase::MIX_SET_LEVEL_ON_SUB_MATERIAL_LEVEL_SET_FLAG ||
|
||
pMixInfo->type == MixBase::MIX_ADD_LEVEL_SET_FLAG ||
|
||
pMixInfo->type == MixBase::MIX_ADD_LEVEL_SET_FLAG_CREATE_ITEM )
|
||
{
|
||
if( pMainMaterial )
|
||
{
|
||
if( pMixInfo->value[2] )
|
||
pMainMaterial->SetInstanceFlagOn( pMixInfo->value[1] );
|
||
else
|
||
pMainMaterial->SetInstanceFlagOff( pMixInfo->value[1] );
|
||
}
|
||
}
|
||
|
||
if( bSuccess )
|
||
{
|
||
if( pMainMaterial )
|
||
{
|
||
pMainMaterial->SetItemEnhance( nEnhance );
|
||
pMainMaterial->SetItemLevel( nLevel );
|
||
}
|
||
|
||
if( pMainMaterial )
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
if( pNewItem )
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pNewItem->GetItemEnhance() * 100 + pNewItem->GetItemLevel(), pNewItem->GetItemCode(), 1, pMixInfo->id, pNewItem->GetCurrentEtherealDurability(), 0, 0, 0, pNewItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
|
||
AR_HANDLE tmp = 0;
|
||
|
||
if( pMainMaterial )
|
||
{
|
||
tmp = pMainMaterial->GetHandle();
|
||
}
|
||
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
}
|
||
else
|
||
{
|
||
if( pMainMaterial )
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "FAIL", LOG::STR_NTS );
|
||
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
}
|
||
|
||
if( pMainMaterial )
|
||
{
|
||
// DB 업데이트, SendItemMessage는 이하에서 담당
|
||
pPlayer->onAfterChangeItemProperty( pMainMaterial );
|
||
}
|
||
|
||
// Save? 이 짓을 해야하나 싶다.. 우선 보류. 나중에 쓸데없는 Save 싹 다 리팩토링 예정
|
||
pPlayer->Save();
|
||
|
||
return true;
|
||
}
|
||
|
||
bool MixManager::RecycleItem( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
// 첫 번째가 장비고, 두 번째가 큐브라는 건 이미 밖에서 검사하고 들어왔음. (Check type/value들에 의해서)
|
||
StructItem * pEquipment = pSubMaterial[0];
|
||
StructItem * pReturnCube = pSubMaterial[1];
|
||
|
||
// 재료 순서 조정 (장비인지 아닌지 구분해야 함)
|
||
if( 9 < pEquipment->GetItemGroup() || pEquipment->GetItemGroup() < 1 )
|
||
{
|
||
StructItem *pTemp = pEquipment;
|
||
pEquipment = pReturnCube;
|
||
pReturnCube = pTemp;
|
||
}
|
||
|
||
// 로그
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "EITEM", LOG::STR_NTS );
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pEquipment->GetItemEnhance() * 100 + pEquipment->GetItemLevel(), pEquipment->GetItemCode(), 1, pEquipment->GetCount() - 1, pMixInfo->id, pEquipment->GetCurrentEtherealDurability(), 0, 0, pEquipment->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "EITEM", LOG::STR_NTS );
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pReturnCube->GetItemEnhance() * 100 + pReturnCube->GetItemLevel(), pReturnCube->GetItemCode(), 1, pReturnCube->GetCount() - 1, pMixInfo->id, pReturnCube->GetCurrentEtherealDurability(), 0, 0, pReturnCube->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "EITEM", LOG::STR_NTS );
|
||
|
||
int nEnhanceEquip = pEquipment->GetItemEnhance();
|
||
|
||
// 어쨌든 큐브는 없앤다(장비는 실패 시에 로그 남긴 이후에 삭제, 성공 시에는 즉시 삭제)
|
||
pPlayer->EraseItem( pReturnCube, 1 );
|
||
|
||
int nRandom = pMixInfo->value[4] * 100;
|
||
|
||
bool bResult = false;
|
||
const char * szRes = "FAIL";
|
||
|
||
if( XRandom( 1, 10000 ) <= nRandom )
|
||
{
|
||
// 성공(장비 아이템 즉시 삭제)
|
||
pPlayer->EraseItem( pEquipment, 1 );
|
||
|
||
// 결과물 몇 개 생성할 건지 결정
|
||
__int64 nResultCnt;
|
||
|
||
// MIX_RECYCLE_ENHANCE일 경우는 10000 : 1로 대응되므로 연산 후에 10000으로 나눠야 개수가 됨
|
||
// 반올림 처리를 단순화하기 위해 1000으로 나누고 5를 더한 후 10으로 나눔(몫만 취하므로 반올림 됨)
|
||
if( pMixInfo->type == MixBase::MIX_RECYCLE_ENHANCE )
|
||
nResultCnt = ( ( XRandom( nEnhanceEquip * pMixInfo->value[2], nEnhanceEquip * pMixInfo->value[3] ) / 1000 ) + 5 ) / 10;
|
||
else
|
||
nResultCnt = XRandom( nEnhanceEquip * pMixInfo->value[2], nEnhanceEquip * pMixInfo->value[3] );
|
||
|
||
// 생성될 개수가 0이면 실패 처리
|
||
if( !nResultCnt )
|
||
{
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
return false;
|
||
}
|
||
|
||
StructItem * pItem = StructItem::AllocItem( 0, pMixInfo->value[0], nResultCnt, ItemInstance::BY_MIX, pMixInfo->value[1] );
|
||
|
||
if( pItem->IsJoinable() )
|
||
{
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@254\v#@item_name@#\v@%d\v#@item_num@#\v%d", pItem->GetItemBase().nNameId, pItem->GetCount() );
|
||
}
|
||
else
|
||
{
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@253\v#@item_name@#\v@%d", pItem->GetItemBase().nNameId );
|
||
}
|
||
|
||
StructItem *pNewItem = pPlayer->PushItem( pItem, pItem->GetCount() );
|
||
if( !pNewItem )
|
||
{
|
||
return bResult;
|
||
}
|
||
|
||
LOG::Log11N4S( LM_ITEM_TAKE, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), pItem->GetCount(), pNewItem->GetCount(), pPlayer->GetGold().GetRawData(), pPlayer->GetGold().GetRawData(), pPlayer->GetX(), pPlayer->GetY(), 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "MIX", LOG::STR_NTS );
|
||
|
||
if( pNewItem != pItem )
|
||
{
|
||
StructItem::PendFreeItem( pItem );
|
||
}
|
||
|
||
pNewItem->TurnOnUpdateFlag();
|
||
|
||
pNewItem->DBQuery( new DB_UpdateItem( pNewItem ) );
|
||
|
||
szRes = "SUCS";
|
||
bResult = true;
|
||
|
||
AR_HANDLE hItem = pNewItem->GetHandle();
|
||
SendMixResult( pPlayer, &hItem, 1 );
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pNewItem->GetItemEnhance() * 100 + pNewItem->GetItemLevel(), pNewItem->GetItemCode(), 1, pMixInfo->id, pNewItem->GetCurrentEtherealDurability(), 0, 0, 0, pNewItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS );
|
||
}
|
||
else
|
||
{
|
||
// 실패 - 따로 처리할 게 없다
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pEquipment->GetItemEnhance() * 100 + pEquipment->GetItemLevel(), pEquipment->GetItemCode(), 1, pMixInfo->id, pEquipment->GetCurrentEtherealDurability(), 0, 0, 0, pEquipment->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS );
|
||
|
||
pPlayer->EraseItem( pEquipment, 1 );
|
||
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
}
|
||
|
||
return bResult;
|
||
}
|
||
|
||
bool MixManager::CreateItem( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
unsigned short nCount = 1;
|
||
if( pMixInfo->type == MixBase::MIX_CREATE_ITEM_IN_MASS )
|
||
{
|
||
nCount = USHRT_MAX;
|
||
|
||
// 605번 유형은 메인 재료가 없어야 한다.
|
||
if( pMainMaterial )
|
||
return false;
|
||
|
||
for( int i = 0 ; i < nSubMaterialCount ; ++i )
|
||
{
|
||
bool bFind = false;
|
||
for( int j = 0 ; j < MixBase::VALUE_COUNT ; ++j )
|
||
{
|
||
bFind = false;
|
||
if( pMixInfo->sub_material[i].type[j] == MIX_CHECK_ITEM_COUNT_GE )
|
||
{
|
||
unsigned short nUnitCount = (unsigned short)pMixInfo->sub_material[i].value[j];
|
||
nCount = std::min( (unsigned short)( pCountList[i] / nUnitCount ), nCount );
|
||
pCountList[i] = nUnitCount;
|
||
bFind = true;
|
||
break;
|
||
}
|
||
}
|
||
// 605번 유형은 반드시 15번 개수 이상 체크 조건이 있어야 한다.
|
||
if( !bFind )
|
||
return false;
|
||
bFind = false;
|
||
}
|
||
|
||
if( nCount > MixBase::MAX_CREATABLE_ITEM )
|
||
{
|
||
nCount = MixBase::MAX_CREATABLE_ITEM;
|
||
}
|
||
}
|
||
|
||
if( pMainMaterial )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, nCount, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "CREATE_ITEM", LOG::STR_NTS );
|
||
pPlayer->EraseItem( pMainMaterial, pMainMaterial->GetCount() );
|
||
}
|
||
|
||
for( int i = 0; i < nSubMaterialCount; ++i )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pSubMaterial[i]->GetItemEnhance() * 100 + pSubMaterial[i]->GetItemLevel(), pSubMaterial[i]->GetItemCode(), 1, pSubMaterial[i]->GetCount() - 1, pMixInfo->id, pSubMaterial[i]->GetCurrentEtherealDurability(), 0, nCount, pSubMaterial[i]->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "CREATE_ITEM", LOG::STR_NTS );
|
||
pPlayer->EraseItem( pSubMaterial[i], pCountList[i] * nCount );
|
||
}
|
||
|
||
std::vector< std::pair< ItemBase::ItemCode, __int64 > > vItemList;
|
||
int nFailCount = 0;
|
||
for( int i = 0 ; i < nCount ; ++i )
|
||
{
|
||
if( pMixInfo->value[ 2 ] > XRandom( 0, 99 ) )
|
||
{
|
||
int nCount = XRandom( pMixInfo->value[3], pMixInfo->value[4] );
|
||
int nResultCount = ( pMixInfo->value[ 0 ] >= 0 ) ? 1 : nCount;
|
||
|
||
for( int i = 0 ; i < nResultCount ; ++i )
|
||
{
|
||
ItemBase::ItemCode nItemID = pMixInfo->value[ 0 ];
|
||
__int64 nItemCount = nCount;
|
||
|
||
while( nItemID < 0 )
|
||
{
|
||
GameContent::SelectItemIDFromDropGroup( nItemID, nItemID, nItemCount );
|
||
}
|
||
|
||
// 구조 트리 또는 해쉬 맵으로 개선 (나중에)
|
||
bool bFind = false;
|
||
for( auto it = vItemList.begin() ; it != vItemList.end() ; ++it )
|
||
{
|
||
if( (*it).first == nItemID )
|
||
{
|
||
bFind = true;
|
||
(*it).second += nItemCount;
|
||
}
|
||
}
|
||
|
||
if( !bFind )
|
||
vItemList.push_back( std::pair< ItemBase::ItemCode, __int64 >( nItemID, nItemCount ) );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
nFailCount++;
|
||
}
|
||
}
|
||
|
||
for( auto it = vItemList.begin() ; it != vItemList.end() ; ++it )
|
||
{
|
||
ItemBase::ItemCode nItemID = (*it).first;
|
||
__int64 nItemCount = (*it).second;
|
||
|
||
// 만약 결과물이 중첩 불가능한 아이템이면 일일이 넣어준다.
|
||
__int64 nLoopCount = 1;
|
||
ItemBase &itemBase = StructItem::GetItemBase( nItemID );
|
||
if( !itemBase.Flag.IsOn( ItemBase::FLAG_JOIN ) )
|
||
{
|
||
nLoopCount = nItemCount;
|
||
nItemCount = 1;
|
||
}
|
||
|
||
for( __int64 i = 0 ; i < nLoopCount ; ++i )
|
||
{
|
||
StructItem * pNewItem = NULL;
|
||
|
||
StructItem * pItem = StructItem::AllocItem( 0, nItemID, nItemCount, ItemInstance::BY_MIX, pMixInfo->value[1] );
|
||
nItemCount = pItem->GetCount();
|
||
|
||
pNewItem = pPlayer->PushItem( pItem, nItemCount );
|
||
if( !pNewItem )
|
||
{
|
||
continue;
|
||
}
|
||
LOG::Log11N4S( LM_ITEM_TAKE, pPlayer->GetAccountID(), pPlayer->GetSID(), pNewItem->GetItemEnhance() * 100 + pNewItem->GetItemLevel(), pNewItem->GetItemCode(), nItemCount, pNewItem->GetCount(), pPlayer->GetGold().GetRawData(), pPlayer->GetGold().GetRawData(), pPlayer->GetX(), pPlayer->GetY(), 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "MIX", LOG::STR_NTS );
|
||
|
||
if( pNewItem != pItem )
|
||
{
|
||
StructItem::PendFreeItem( pItem );
|
||
}
|
||
|
||
vCreatedItem->push_back( std::make_pair( pNewItem, nItemCount ) );
|
||
|
||
if( pNewItem )
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pNewItem->GetItemEnhance() * 100 + pNewItem->GetItemLevel(), pNewItem->GetItemCode(), nCount, pMixInfo->id, pNewItem->GetCurrentEtherealDurability(), 0, 0, 0, pNewItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
else
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), 0, 0, 0, pMixInfo->id, 0, 0, 0, 0, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
}
|
||
}
|
||
|
||
if( nFailCount > 0 )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), 0, 0, 0, pMixInfo->id, 0, 0, 0, nFailCount, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "FAIL_COUNT", LOG::STR_NTS );
|
||
}
|
||
|
||
if( vCreatedItem->empty() )
|
||
{
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
}
|
||
else
|
||
{
|
||
AR_HANDLE hItem = NULL;
|
||
for( auto it = vCreatedItem->begin() ; it != vCreatedItem->end() ; ++it )
|
||
{
|
||
StructItem *pItem = (*it).first;
|
||
__int64 nCount = (*it).second;
|
||
hItem = pItem->GetHandle();
|
||
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@254\v#@item_name@#\v@%d\v#@item_num@#\v%d", pItem->GetItemBase().nNameId, nCount );
|
||
}
|
||
SendMixResult( pPlayer, &hItem, 1 );
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
bool MixManager::ChangeAppearanceCode( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
int nAppearanceCode;
|
||
|
||
if( pMixInfo->type == MixBase::MIX_CHANGE_APPEARANCE_CODE )
|
||
{
|
||
nAppearanceCode = pSubMaterial[0]->GetItemCode();
|
||
}
|
||
else if( pMixInfo->type == MixBase::MIX_REMOVE_APPEARANCE_CODE )
|
||
{
|
||
nAppearanceCode = 0;
|
||
}
|
||
|
||
// 현재는 실패하는 경우가 없으나 만약 값 설정에 실패하였을 때 재료가 사라져서는 안 되므로 확인을 한다.
|
||
if( !pMainMaterial->SetAppearanceCode( nAppearanceCode ) )
|
||
{
|
||
SendResult( pPlayer, pMixInfo->id, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 로그 및 재료 아이템 제거
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "CHANGE_APPEARANCE", LOG::STR_NTS );
|
||
|
||
for( int i = 0; i < nSubMaterialCount; ++i )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pSubMaterial[i]->GetItemEnhance() * 100 + pSubMaterial[i]->GetItemLevel(), pSubMaterial[i]->GetItemCode(), 1, pSubMaterial[i]->GetCount() - 1, pMixInfo->id, pSubMaterial[i]->GetCurrentEtherealDurability(), 0, 0, pSubMaterial[i]->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "CHANGE_APPEARANCE", LOG::STR_NTS );
|
||
pPlayer->EraseItem( pSubMaterial[i], pCountList[i] );
|
||
}
|
||
|
||
SendItemMessage( pPlayer, pMainMaterial );
|
||
pMainMaterial->DBQuery( new DB_UpdateItem( pMainMaterial ) );
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
|
||
return true;
|
||
}
|
||
|
||
bool MixManager::ChangeItemCode( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
int nItemCode = pMainMaterial->GetItemCode();
|
||
|
||
switch( pMixInfo->type )
|
||
{
|
||
case MixBase::MIX_REPLACE_WITH_RANDOM:
|
||
nItemCode = pMixInfo->value[1] + XRandom( 0, pMixInfo->value[2] );
|
||
break;
|
||
|
||
case MixBase::MIX_CHANGE_CODE_ADD:
|
||
nItemCode += pMixInfo->value[1];
|
||
break;
|
||
|
||
default:
|
||
nItemCode = pMixInfo->value[1];
|
||
break;
|
||
}
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "CHANGE_CODE", LOG::STR_NTS );
|
||
for( int i = 0; i < nSubMaterialCount; ++i )
|
||
{
|
||
StructItem *pItem = pSubMaterial[i];
|
||
unsigned short nCount = pCountList[i];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), nCount, pItem->GetCount() - nCount, pMixInfo->id, pItem->GetCurrentEtherealDurability(), 0, 0, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "CHANGE_CODE", LOG::STR_NTS );
|
||
|
||
pPlayer->EraseItem( pItem, nCount );
|
||
}
|
||
|
||
int nRandom = pMixInfo->value[0] * 100;
|
||
|
||
bool bResult = false;
|
||
const char * szRes = "FAIL";
|
||
|
||
if( XRandom( 1, 10000 ) <= nRandom )
|
||
{
|
||
// 현재는 실패하는 경우가 없으나 만약 값 설정에 실패하였을 때 재료가 사라져서는 안 되므로 확인을 한다.
|
||
if (!pMainMaterial->ChangeItemCode( nItemCode ))
|
||
{
|
||
SendResult( pPlayer, pMixInfo->id, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
pMainMaterial = pPlayer->onAfterChangeItemProperty( pMainMaterial );
|
||
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
|
||
szRes = "SUCS";
|
||
bResult = true;
|
||
|
||
// 로그 및 재료 아이템 제거
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS );
|
||
}
|
||
else
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS );
|
||
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
}
|
||
|
||
return bResult;
|
||
}
|
||
|
||
bool MixManager::ChangeItemUsablePeriod( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
// MIX_CHANGE_USABLE_PERIOD 조합유형
|
||
// value[0]: 0일때 초기화, 1일때 value[1]에 적힌 기간만큼 기간 연장
|
||
|
||
if( pMixInfo->value[0] == 0 )
|
||
{
|
||
pMainMaterial->SetRemainTime( time( NULL ) + pMainMaterial->GetItemBase().available_time );
|
||
}
|
||
else if( pMixInfo->value[0] == 1 && pMixInfo->value[1] > 0 )
|
||
{
|
||
int nExtendingTime = pMixInfo->value[1];
|
||
|
||
// remain_time늘려줄 때 int_max 값을 넘어버리면 안 해줌. (DB에 남은시간을 int로 넣기때문)
|
||
time_t remain_time = 0;
|
||
if( pMainMaterial->GetItemBase().decrease_type == ItemBase::DECREASE_ALWAYS )
|
||
{
|
||
remain_time = pMainMaterial->GetExpireTime() + nExtendingTime;
|
||
}
|
||
else
|
||
{
|
||
remain_time = pMainMaterial->GetExpireTime() - time( NULL ) + nExtendingTime;
|
||
}
|
||
if( remain_time > INT_MAX )
|
||
{
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_ENHANCE_LIMIT );
|
||
return false;
|
||
}
|
||
|
||
pMainMaterial->SetRemainTime( pMainMaterial->GetExpireTime() + nExtendingTime );
|
||
}
|
||
else
|
||
{
|
||
// 조합식 오류
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
// 로그 및 재료 아이템 제거
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "CHANGE_APPEARANCE", LOG::STR_NTS );
|
||
|
||
for( int i = 0; i < nSubMaterialCount; ++i )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pSubMaterial[i]->GetItemEnhance() * 100 + pSubMaterial[i]->GetItemLevel(), pSubMaterial[i]->GetItemCode(), 1, pSubMaterial[i]->GetCount() - 1, pMixInfo->id, pSubMaterial[i]->GetCurrentEtherealDurability(), 0, 0, pSubMaterial[i]->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "EXTEND_ITEM_TIME", LOG::STR_NTS );
|
||
pPlayer->EraseItem( pSubMaterial[i], pCountList[i] );
|
||
}
|
||
|
||
SendItemMessage( pPlayer, pMainMaterial );
|
||
pMainMaterial->DBQuery( new DB_UpdateItem( pMainMaterial ) );
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetExpireTime(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
bool MixManager::SetElementalEffect( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
// 속성 이펙트 적용 아이템과 속성 이펙트 적용 재료가 있고 적절한 아이템인지는 조합식의 체크 조건에 의해 검사되어 있음
|
||
|
||
StructItem * pElementalEffector = pSubMaterial[0];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "ELEMENTAL_EFFECT", LOG::STR_NTS );
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pElementalEffector->GetItemEnhance() * 100 + pElementalEffector->GetItemLevel(), pElementalEffector->GetItemCode(), 1, pElementalEffector->GetCount(), pMixInfo->id, pElementalEffector->GetCurrentEtherealDurability(), 0, 0, pElementalEffector->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "ELEMENTAL_EFFECT", LOG::STR_NTS );
|
||
|
||
unsigned char cElementalType = pElementalEffector->GetItemBase().fOptVar1[ 0 ].GetAsInt64();
|
||
if( !cElementalType )
|
||
{
|
||
if( pMainMaterial->GetElementalEffectType() && pMainMaterial->GetElementalEffectExpireTime() )
|
||
{
|
||
pPlayer->RemoveFromElementalEffectedItemList( pMainMaterial );
|
||
}
|
||
pMainMaterial->ClearElementalEffect();
|
||
}
|
||
else
|
||
{
|
||
time_t tExpire = pElementalEffector->GetItemBase().fOptVar2[ 0 ] ? time( NULL ) + pElementalEffector->GetItemBase().fOptVar2[ 0 ] : 0;
|
||
|
||
if( pMainMaterial->SetElementalEffect( cElementalType, tExpire ) != RESULT_SUCCESS )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "FAIL", LOG::STR_NTS );
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
return false;
|
||
}
|
||
|
||
// 만약 영구 속성이 부여된 아이템이라면 만료 처리를 하지 않기 위해 Elemental Effected Item List에 추가해서는 안 될 뿐더러 행여나 존재할 경우에는 제거를 해줘야 한다.
|
||
if( tExpire )
|
||
pPlayer->AddToElementalEffectedItemList( pMainMaterial );
|
||
else
|
||
pPlayer->RemoveFromElementalEffectedItemList( pMainMaterial );
|
||
}
|
||
pMainMaterial->TurnOnUpdateFlag();
|
||
|
||
pPlayer->EraseItem( pElementalEffector, 1 );
|
||
|
||
SendItemMessage( pPlayer, pMainMaterial );
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
|
||
return true;
|
||
}
|
||
|
||
bool MixManager::SetElementalEffectParameter( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
// 속성 이펙트 적용된 아이템과 속성 이펙트 추가 성능 적용 재료가 있고 적절한 아이템인지는 조합식의 체크 조건에 의해 검사되어 있음
|
||
|
||
StructItem * pElementalEffectEnhancer = pSubMaterial[0];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "ELEMENTAL_EFFECT_ENHANCE", LOG::STR_NTS );
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pElementalEffectEnhancer->GetItemEnhance() * 100 + pElementalEffectEnhancer->GetItemLevel(), pElementalEffectEnhancer->GetItemCode(), 1, pElementalEffectEnhancer->GetCount(), pMixInfo->id, pElementalEffectEnhancer->GetCurrentEtherealDurability(), 0, 0, pElementalEffectEnhancer->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "ELEMENTAL_EFFECT_ENHANCE", LOG::STR_NTS );
|
||
|
||
std::string strResult = "";
|
||
|
||
// 공격력 세팅 체크(최소/최대 값 중에 0이지 않은 값이 있는 경우만 적용)
|
||
int nMinValue = pElementalEffectEnhancer->GetItemBase().fOptVar1[ 0 ];
|
||
int nMaxValue = pElementalEffectEnhancer->GetItemBase().fOptVar2[ 0 ];
|
||
if( nMinValue || nMaxValue )
|
||
{
|
||
int nAttackPoint = XRandom( nMinValue, nMaxValue );
|
||
pMainMaterial->SetElementalEffectAttackPoint( nAttackPoint );
|
||
|
||
XStringUtil::Format( strResult, "Attack:%d ", nAttackPoint );
|
||
}
|
||
|
||
// 마력 세팅 체크(최소/최대 값 중에 0이지 않은 값이 있는 경우만 적용)
|
||
nMinValue = pElementalEffectEnhancer->GetItemBase().fOptVar1[ 1 ];
|
||
nMaxValue = pElementalEffectEnhancer->GetItemBase().fOptVar2[ 1 ];
|
||
if( nMinValue || nMaxValue )
|
||
{
|
||
int nMagicPoint = XRandom( nMinValue, nMaxValue );
|
||
pMainMaterial->SetElementalEffectMagicPoint( nMagicPoint );
|
||
|
||
XStringUtil::Format( strResult, "%sMagic:%d", strResult.c_str(), nMagicPoint );
|
||
}
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, strResult.c_str(), LOG::STR_NTS );
|
||
|
||
pMainMaterial->TurnOnUpdateFlag();
|
||
|
||
pPlayer->EraseItem( pElementalEffectEnhancer, 1 );
|
||
|
||
SendItemMessage( pPlayer, pMainMaterial );
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
|
||
return true;
|
||
}
|
||
|
||
bool MixManager::SetSocket( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
int index = -1;
|
||
for( int i = 0; i < ItemBase::MAX_SOCKET_NUMBER; ++i )
|
||
{
|
||
if( pMixInfo->value[0] == pMainMaterial->GetSocketCode( i ) )
|
||
{
|
||
index = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 대상 소울스톤 ID를 찾지 못 했으면 실패
|
||
if( index == -1 ) return false;
|
||
|
||
// 로그 및 재료 아이템 제거
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, index, pMixInfo->value[0], 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SET_SOCKET", LOG::STR_NTS );
|
||
|
||
for( int i = 0; i < nSubMaterialCount; ++i )
|
||
{
|
||
StructItem *pItem = pSubMaterial[i];
|
||
unsigned short nCount = pCountList[i];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), nCount, pItem->GetCount() - nCount, pMixInfo->id, 0, 0, 0, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SET_SOCKET", LOG::STR_NTS );
|
||
|
||
pPlayer->EraseItem( pItem, nCount );
|
||
}
|
||
|
||
bool bSuccess = XRandom( 0, 99 ) < pMixInfo->value[2];
|
||
const char *pszResult = ( bSuccess ) ? "SUCS" : "FAIL";
|
||
int code = ( bSuccess ) ? pMixInfo->value[1] : pMixInfo->value[3];
|
||
|
||
if( pMixInfo->value[4] && pMixInfo->value[0] == 0 && code != 0 )
|
||
{
|
||
// 대상 아이템이 없음
|
||
assert( StructItem::GetItemBase( code ).nCode );
|
||
pMainMaterial->SetCurrentEndurance( pMainMaterial->GetCurrentEndurance() + StructItem::GetItemBase( code ).nEndurance );
|
||
}
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, index, code, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, pszResult, LOG::STR_NTS );
|
||
|
||
pMainMaterial->SetSocketCode( index, code );
|
||
pMainMaterial->TurnOnUpdateFlag();
|
||
pMainMaterial->DBQuery( new DB_UpdateItem( pMainMaterial ) );
|
||
|
||
if( bSuccess && pMixInfo->value[5] )
|
||
{
|
||
// 성공 시 아이템 할당
|
||
ItemBase::ItemCode nItemID = pMixInfo->value[5];
|
||
__int64 nItemCount = 1;
|
||
|
||
while( nItemID < 0 )
|
||
{
|
||
GameContent::SelectItemIDFromDropGroup( nItemID, nItemID, nItemCount );
|
||
}
|
||
|
||
if( nItemID > 0 )
|
||
{
|
||
StructItem *pItem = StructItem::AllocItem( 0, nItemID, nItemCount, ItemInstance::BY_MIX );
|
||
|
||
if( pItem->IsJoinable() )
|
||
{
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@254\v#@item_name@#\v@%d\v#@item_num@#\v%d", pItem->GetItemBase().nNameId, pItem->GetCount() );
|
||
}
|
||
else
|
||
{
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@253\v#@item_name@#\v@%d", pItem->GetItemBase().nNameId );
|
||
}
|
||
|
||
StructItem *pNewItem = pPlayer->PushItem( pItem, pItem->GetCount() );
|
||
if( pNewItem )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_TAKE, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), pItem->GetCount(), pNewItem->GetCount(), pPlayer->GetGold().GetRawData(), pPlayer->GetGold().GetRawData(), pPlayer->GetX(), pPlayer->GetY(), 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "MIX", LOG::STR_NTS );
|
||
}
|
||
|
||
if( pNewItem != pItem )
|
||
{
|
||
StructItem::PendFreeItem( pItem );
|
||
}
|
||
}
|
||
}
|
||
|
||
if (bSuccess)
|
||
{
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult(pPlayer, &tmp, 1);
|
||
PrintfChatMessage(false, CHAT_ITEM, "@SYSTEM", pPlayer, "@9715");
|
||
|
||
}
|
||
else
|
||
{
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult(pPlayer, &tmp, 1);
|
||
PrintfChatMessage(false, CHAT_ITEM, "@SYSTEM", pPlayer, "@9716");
|
||
}
|
||
|
||
SendItemMessage(pPlayer, pMainMaterial);
|
||
|
||
//SendItemMessage( pPlayer, pMainMaterial );
|
||
//AR_HANDLE hItem = pMainMaterial->GetHandle();
|
||
//SendMixResult( pPlayer, &hItem, bSuccess );
|
||
|
||
return bSuccess;
|
||
}
|
||
|
||
bool MixManager::ReplaceSocketWith( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
// 존재하지 않는 보조 재료를 택함
|
||
if( pMixInfo->value[0] < 1 || pMixInfo->value[0] > nSubMaterialCount )
|
||
{
|
||
assert( 0 );
|
||
SendResult( pPlayer, TM_CS_MIX, RESULT_INVALID_ARGUMENT );
|
||
return false;
|
||
}
|
||
|
||
int nTargetIndex = pMixInfo->value[0];
|
||
bool bErase = false;
|
||
bool bReset = false;
|
||
StructItem *pTargetItem = pSubMaterial[nTargetIndex - 1];
|
||
|
||
// 주 재료에 대한 로그
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "REPLACE_SOCKET_WITH", LOG::STR_NTS );
|
||
|
||
// 나머지 재료에 대한 로그 및 삭제
|
||
// 대상 재료는 성공 여부에 따라 로그 포맷이 바뀌므로 따로 처리한다.
|
||
for( int i = 0; i < nSubMaterialCount; ++i )
|
||
{
|
||
if( i == nTargetIndex - 1 ) continue;
|
||
|
||
StructItem *pItem = pSubMaterial[i];
|
||
unsigned short nCount = pCountList[i];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), nCount, pItem->GetCount() - nCount, pMixInfo->id, 0, 0, 0, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SET_SOCKET", LOG::STR_NTS );
|
||
|
||
pPlayer->EraseItem( pItem, nCount );
|
||
}
|
||
|
||
// 소켓 정보 이전
|
||
bool bSuccess = XRandom( 0, 99 ) < pMixInfo->value[1];
|
||
const char *pszResult = ( bSuccess ) ? "SUCS" : "FAIL";
|
||
if( bSuccess )
|
||
{
|
||
for( int i = 0; i < pMainMaterial->GetMaxSocketCount(); i++ )
|
||
{
|
||
pMainMaterial->SetSocketCode( i, pTargetItem->GetSocketCode( i ) );
|
||
}
|
||
|
||
pMainMaterial->DBQuery( new DB_UpdateItem( pMainMaterial ) );
|
||
|
||
bErase = ( pMixInfo->value[2] == MixBase::RESULT_SOCKET_ERASE );
|
||
bReset = ( pMixInfo->value[2] == MixBase::RESULT_SOCKET_RESET );
|
||
}
|
||
else
|
||
{
|
||
bErase = ( pMixInfo->value[3] == MixBase::RESULT_SOCKET_ERASE );
|
||
bReset = ( pMixInfo->value[3] == MixBase::RESULT_SOCKET_RESET );
|
||
}
|
||
|
||
// 대상 재료에 대한 정책
|
||
if( bErase )
|
||
{
|
||
unsigned short nCount = pCountList[nTargetIndex - 1];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pTargetItem->GetItemEnhance() * 100 + pTargetItem->GetItemLevel(), pTargetItem->GetItemCode(), 1, 0, pMixInfo->id, 0, 0, 0, pTargetItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "REPLACE_SOCKET_WITH", LOG::STR_NTS );
|
||
|
||
pPlayer->EraseItem( pTargetItem, nCount );
|
||
}
|
||
else if( bReset )
|
||
{
|
||
// 옮겨진 소켓과는 상관없이 모든 소켓이 초기화된다.
|
||
for( int i = 0; i < pTargetItem->GetMaxSocketCount(); i++ )
|
||
{
|
||
pTargetItem->SetSocketCode( i, 0 );
|
||
}
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pTargetItem->GetItemEnhance() * 100 + pTargetItem->GetItemLevel(), pTargetItem->GetItemCode(), 1, 1, pMixInfo->id, 0, 0, 0, pTargetItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "REPLACE_SOCKET_WITH", LOG::STR_NTS );
|
||
LOG::Log11N4S( LM_ITEM_SOCKET_INFO, pPlayer->GetAccountID(), pPlayer->GetSID(), pTargetItem->GetCurrentEndurance() * 100 + pTargetItem->GetItemLevel(), pTargetItem->GetMaxEndurance(), pTargetItem->GetSocketCode( 0 ), pTargetItem->GetSocketCode( 1 ), pTargetItem->GetSocketCode( 2 ), pTargetItem->GetSocketCode( 3 ), pTargetItem->GetSocketCode( 4 ), pTargetItem->GetSocketCode( 5 ), pTargetItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "", 0 );
|
||
|
||
pTargetItem->DBQuery( new DB_UpdateItem( pTargetItem ) );
|
||
SendItemMessage( pPlayer, pTargetItem );
|
||
}
|
||
|
||
// 결과 로그
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, pszResult, LOG::STR_NTS );
|
||
LOG::Log11N4S( LM_ITEM_SOCKET_INFO, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetCurrentEndurance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetMaxEndurance(), pMainMaterial->GetSocketCode( 0 ), pMainMaterial->GetSocketCode( 1 ), pMainMaterial->GetSocketCode( 2 ), pMainMaterial->GetSocketCode( 3 ), pMainMaterial->GetSocketCode( 4 ), pMainMaterial->GetSocketCode( 5 ), pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "", 0 );
|
||
|
||
SendItemMessage( pPlayer, pMainMaterial );
|
||
AR_HANDLE hItem = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &hItem, bSuccess );
|
||
|
||
return bSuccess;
|
||
}
|
||
|
||
bool MixManager::SacrificeItemForEtherealDurability( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
bool bSuccess = false;
|
||
|
||
for( int i = 0 ; i < nSubMaterialCount ; ++i )
|
||
{
|
||
// 완충 상태면 충전 시도 중지(여러 개 충전 중간에 완충 상태가 될 수도 있으므로 매 루프마다 검사)
|
||
if( pMainMaterial->GetCurrentEtherealDurability() >= pMainMaterial->GetMaxEtherealDurability() )
|
||
break;
|
||
|
||
StructItem * pSacrifice = pSubMaterial[ i ];
|
||
unsigned short nCount = pCountList[ i ];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SACRIFICE_ITEM_FOR_ETHEREAL_DURABILITY", LOG::STR_NTS );
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pSacrifice->GetItemEnhance() * 100 + pSacrifice->GetItemLevel(), pSacrifice->GetItemCode(), nCount, pSacrifice->GetCount() - nCount, pMixInfo->id, 0, 0, 0, pSacrifice->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SACRIFICE_ITEM_FOR_ETHEREAL_DURABILITY", LOG::STR_NTS );
|
||
|
||
// 1번째 보조 재료는 복구용이 아니고 그냥 소모용 재료임(고정 소모 재료 아이템에 대한 결과 로그는 남지 않음)
|
||
if( i != 0 )
|
||
{
|
||
// 성공률 체크
|
||
if( XRandom( 0, 99 ) < pMixInfo->value[ 3 ] )
|
||
{
|
||
bSuccess = true;
|
||
|
||
__int64 nRecover = pSacrifice->GetItemBase().nPrice.GetRawData() * pMixInfo->value[ 0 ] / 100 * nCount;
|
||
nRecover += pMixInfo->value[ 1 ];
|
||
|
||
// 파괴된 보조 재료템은 25%만 적용
|
||
if( pSacrifice->GetMaxEtherealDurability() && !pSacrifice->GetCurrentEtherealDurability() )
|
||
nRecover /= 4;
|
||
|
||
pMainMaterial->AddCurrentEtherealDurability( static_cast< int >( nRecover ) );
|
||
if( pMixInfo->type == MixBase::MIX_SACRIFICE_ITEM_FOR_ETHEREAL_DURABILITY_WITH_MESSAGE )
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@7900\v#@Ethereal_Durability@#\v%d", nRecover / 10000 );
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
}
|
||
else
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "FAIL", LOG::STR_NTS );
|
||
}
|
||
}
|
||
|
||
// 보호 대상 아이템이 아닌 경우만 희생 아이템 삭제
|
||
if( ( pMixInfo->value[ 2 ] & ( 1 << i ) ) == 0 )
|
||
pPlayer->EraseItem( pSacrifice, nCount );
|
||
}
|
||
|
||
if( bSuccess )
|
||
{
|
||
SendItemMessage( pPlayer, pMainMaterial );
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
}
|
||
else
|
||
{
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
}
|
||
|
||
return bSuccess;
|
||
}
|
||
|
||
bool MixManager::SacrificeItemForEtherealStoneDurability( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
bool bSuccess = false;
|
||
__int64 nRecover = 0;
|
||
|
||
for( int i = 0 ; i < nSubMaterialCount ; ++i )
|
||
{
|
||
// 완충 상태면 충전 시도 중지(여러 개 충전 중간에 완충 상태가 될 수도 있으므로 매 루프마다 검사)
|
||
if( pPlayer->GetEtherealStoneDurability() >= GameRule::MAX_ETHEREAL_STONE_DURABILITY )
|
||
break;
|
||
|
||
StructItem * pSacrifice = pSubMaterial[ i ];
|
||
unsigned short nCount = pCountList[ i ];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pSacrifice->GetItemEnhance() * 100 + pSacrifice->GetItemLevel(), pSacrifice->GetItemCode(), nCount, pSacrifice->GetCount() - nCount, pMixInfo->id, 0, 0, 0, pSacrifice->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SACRIFICE_ITEM_FOR_ETHEREAL_STONE_DURABILITY", LOG::STR_NTS );
|
||
|
||
// 성공률 체크
|
||
if( XRandom( 0, 99 ) < pMixInfo->value[ 3 ] )
|
||
{
|
||
bSuccess = true;
|
||
|
||
__int64 nCurrentRecover = pSacrifice->GetItemBase().nPrice.GetRawData() * pMixInfo->value[ 0 ] / 100 * nCount;
|
||
nCurrentRecover += pMixInfo->value[ 1 ];
|
||
|
||
// 파괴된 보조 재료템은 25%만 적용
|
||
if( pSacrifice->GetMaxEtherealDurability() && !pSacrifice->GetCurrentEtherealDurability() )
|
||
nCurrentRecover /= 4;
|
||
|
||
int nPrevEtherealStoneDurability = pPlayer->GetEtherealStoneDurability();
|
||
|
||
pPlayer->AddEtherealStoneDurability( static_cast< int >( nCurrentRecover ) );
|
||
nRecover += nCurrentRecover;
|
||
|
||
// 메인 아이템은 없고 보조 아이템은 Erase 로그가 남기 때문에, 성공 여부와 함께 플레이어의 정보를 남긴다.
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), 0, 0, 1, pMixInfo->id, pPlayer->GetEtherealStoneDurability(), 0, 0, 0, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
|
||
LOG::Log11N4S( LM_CHARGE_ETHEREAL_DURABILITY, pPlayer->GetAccountID(), pPlayer->GetSID(), nPrevEtherealStoneDurability, pPlayer->GetEtherealStoneDurability(), 0, 0, 0, 0, 0, 0, pSacrifice->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "", 0 );
|
||
}
|
||
else
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), 0, 0, 1, pMixInfo->id, pPlayer->GetEtherealStoneDurability(), 0, 0, 0, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "FAIL", LOG::STR_NTS );
|
||
}
|
||
|
||
|
||
// 보호 대상 아이템이 아닌 경우만 희생 아이템 삭제
|
||
if( ( pMixInfo->value[ 2 ] & ( 1 << i ) ) == 0 )
|
||
pPlayer->EraseItem( pSacrifice, nCount );
|
||
}
|
||
|
||
if( bSuccess )
|
||
{
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@7900\v#@Ethereal_Durability@#\v%d", nRecover / 10000 );
|
||
}
|
||
else
|
||
{
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
}
|
||
|
||
return bSuccess;
|
||
}
|
||
|
||
bool MixManager::TransmitEtherealDurability( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
StructItem * pBattery = pSubMaterial[ 0 ];
|
||
StructItem * pCatalyst = pSubMaterial[ 1 ];
|
||
int nDedicatedCatalystCount = pCountList[ 1 ];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "TRANSMIT_ETHEREAL_DURABILITY", LOG::STR_NTS );
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pBattery->GetItemEnhance() * 100 + pBattery->GetItemLevel(), pBattery->GetItemCode(), 1, pBattery->GetCount(), pMixInfo->id, 0, 0, 0, pBattery->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "TRANSMIT_ETHEREAL_DURABILITY", LOG::STR_NTS );
|
||
|
||
// 툴팁에서 보여지는 단위로 처리(실제 수치의 1/10000)
|
||
int nUsedEtherealDurability = ( pMainMaterial->GetMaxEtherealDurability() - pMainMaterial->GetCurrentEtherealDurability() ) / 10000;
|
||
|
||
// 회복될 장비 아이템의 내구도가 최소 복구 단위 이상 소진되어 있다는 조건은 조합 DB에서 걸려있지만 그냥 한 번 더 'ㅠ '
|
||
// 회복될 장비가 아예 내구도가 바닥났으면 여기서 복구 안 해줌(RecoverExhaustedEtherealDurability에 의해 복구시켜야 함)
|
||
if( !nUsedEtherealDurability || !pMainMaterial->GetCurrentEtherealDurability() )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "FAIL", LOG::STR_NTS );
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
return false;
|
||
}
|
||
|
||
// 충전율에 따라 최대 충전에 소요될 보조 재료 및 에테리얼 내구도 계산
|
||
c_fixed10 fRequiredEtherealDurability( nUsedEtherealDurability );
|
||
fRequiredEtherealDurability = ( fRequiredEtherealDurability - pMixInfo->value[ 1 ] ) * 100 / pMixInfo->value[ 0 ];
|
||
|
||
// 실제 에테리얼 충전용 아이템에서 꺼낼 수 있는 에테리얼 양을 얻고 다시 해당 결과와 소모성 재료의 개수를 기준으로 비교(개수 = 회복량으로 사용됨)
|
||
// * 충전용 아이템에 50000 의 내구도가 있을 경우 최대 회복 가능 량은 50000이 아닌 40000임(현재 충전 량보다 적은 양만 사용 가능)
|
||
int nExtractAmount = std::min( static_cast< int >( fRequiredEtherealDurability ), ( pBattery->GetCurrentEtherealDurability() - 1 ) / 10000 );
|
||
nExtractAmount = std::min( nExtractAmount, nDedicatedCatalystCount );
|
||
|
||
// 소모될 촉매의 개수(=복구될 에테리얼 내구도) 체크
|
||
if( !nExtractAmount || pCatalyst->GetCount() < nExtractAmount || ( pBattery->GetCurrentEtherealDurability() / 10000 ) < nExtractAmount )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "FAIL", LOG::STR_NTS );
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
return false;
|
||
}
|
||
|
||
// 실제 촉매 소모량을 결정한 이후에 촉매에 대한 조합 시도 로그 생성
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pCatalyst->GetItemEnhance() * 100 + pCatalyst->GetItemLevel(), pCatalyst->GetItemCode(), nExtractAmount, pCatalyst->GetCount() - nExtractAmount, pMixInfo->id, 0, 0, 0, pCatalyst->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "TRANSMIT_ETHEREAL_DURABILITY", LOG::STR_NTS );
|
||
|
||
pBattery->AddCurrentEtherealDurability( -1 * nExtractAmount * 10000 );
|
||
SendItemMessage( pPlayer, pBattery );
|
||
pMainMaterial->AddCurrentEtherealDurability( nExtractAmount * 10000 * pMixInfo->value[ 0 ] / 100 + pMixInfo->value[ 1 ] );
|
||
SendItemMessage( pPlayer, pMainMaterial );
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@7901\v#@Ethereal_Durability@#\v%d", ( nExtractAmount * 10000 * pMixInfo->value[ 0 ] / 100 + pMixInfo->value[ 1 ] ) / 10000 );
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pBattery->GetItemEnhance() * 100 + pBattery->GetItemLevel(), pBattery->GetItemCode(), 1, pMixInfo->id, pBattery->GetCurrentEtherealDurability(), 0, 0, 0, pBattery->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
|
||
// 보호 대상 아이템이 아닌 경우만 희생 아이템 삭제(배터리가 삭제되는 수도 있으므로 모든 충전 처리 이후에 삭제)
|
||
for( int i = 0 ; i < nSubMaterialCount ; ++i )
|
||
{
|
||
if( ( pMixInfo->value[ 2 ] & ( 1 << i ) ) == 0 )
|
||
pPlayer->EraseItem( pSubMaterial[ i ], ( pSubMaterial[ i ] == pCatalyst ) ? nExtractAmount : pCountList[ i ] );
|
||
}
|
||
|
||
AR_HANDLE hResults[] = { pBattery->GetHandle(), pMainMaterial->GetHandle() };
|
||
SendMixResult( pPlayer, hResults, _countof( hResults ) );
|
||
return true;
|
||
}
|
||
|
||
bool MixManager::TransmitEtherealDurabilityFromEtherealStone( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "TRANSMIT_ETHEREAL_DURABILITY_FROM_ETHEREAL_STONE", LOG::STR_NTS );
|
||
|
||
// 툴팁에서 보여지는 단위로 처리(실제 수치의 1/10000)
|
||
int nUsedEtherealDurability = ( pMainMaterial->GetMaxEtherealDurability() - pMainMaterial->GetCurrentEtherealDurability() ) / 10000;
|
||
|
||
// 회복될 장비 아이템의 내구도가 최소 복구 단위 이상 소진되어 있다는 조건은 조합 DB에서 걸려있지만 그냥 한 번 더 'ㅠ '
|
||
// 회복될 장비가 아예 내구도가 바닥났으면 여기서 복구 안 해줌(RecoverExhaustedEtherealDurability에 의해 복구시켜야 함)
|
||
if( !nUsedEtherealDurability || !pMainMaterial->GetCurrentEtherealDurability() )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "FAIL", LOG::STR_NTS );
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
return false;
|
||
}
|
||
|
||
// 충전율에 따라 최대 충전에 소요될 보조 재료 및 에테리얼 내구도 계산
|
||
c_fixed10 fRequiredEtherealDurability( nUsedEtherealDurability );
|
||
fRequiredEtherealDurability = ( fRequiredEtherealDurability - pMixInfo->value[ 1 ] ) * 100 / pMixInfo->value[ 0 ];
|
||
|
||
// 에테리얼 스톤에서 꺼낼 수 있는 에테리얼 양을 얻는다.
|
||
//*에테리얼 스톤에 50000 의 내구도가 있을 경우 최대 회복 가능 량은 50000이 아닌 40000임(현재 충전 량보다 적은 양만 사용 가능)
|
||
int nExtractAmount = std::min( static_cast< int >( fRequiredEtherealDurability ), ( pPlayer->GetEtherealStoneDurability() - 1 ) / 10000 );
|
||
|
||
// 복구될 에테리얼 내구도 체크
|
||
if( !nExtractAmount || ( pPlayer->GetEtherealStoneDurability() / 10000 ) < nExtractAmount )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "FAIL", LOG::STR_NTS );
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
return false;
|
||
}
|
||
|
||
int nPrevEtherealStoneDurability = pPlayer->GetEtherealStoneDurability();
|
||
int nPrevItemEtherealDurability = pMainMaterial->GetCurrentEtherealDurability();
|
||
|
||
pPlayer->AddEtherealStoneDurability( -1 * nExtractAmount * 10000 );
|
||
pMainMaterial->AddCurrentEtherealDurability( nExtractAmount * 10000 * pMixInfo->value[ 0 ] / 100 + pMixInfo->value[ 1 ] );
|
||
SendItemMessage( pPlayer, pMainMaterial );
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@7901\v#@Ethereal_Durability@#\v%d", ( nExtractAmount * 10000 * pMixInfo->value[ 0 ] / 100 + pMixInfo->value[ 1 ] ) / 10000 );
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
|
||
LOG::Log11N4S( LM_TRANSMIT_ETHEREAL_DURABILITY, pPlayer->GetAccountID(), pPlayer->GetSID(), nPrevEtherealStoneDurability, pPlayer->GetEtherealStoneDurability(), nPrevItemEtherealDurability, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "", 0 );
|
||
|
||
// 보호 대상 아이템이 아닌 경우만 희생 아이템 삭제(배터리가 삭제되는 수도 있으므로 모든 충전 처리 이후에 삭제)
|
||
for( int i = 0 ; i < nSubMaterialCount ; ++i )
|
||
{
|
||
if( ( pMixInfo->value[ 2 ] & ( 1 << i ) ) == 0 )
|
||
pPlayer->EraseItem( pSubMaterial[ i ], pCountList[ i ] );
|
||
}
|
||
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
return true;
|
||
}
|
||
|
||
bool MixManager::RecoverExhaustedEtherealDurability( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "TRANSMIT_ETHEREAL_DURABILITY", LOG::STR_NTS );
|
||
|
||
for( int i = 0 ; i < nSubMaterialCount ; ++i )
|
||
{
|
||
StructItem * pItem = pSubMaterial[ i ];
|
||
unsigned short nCount = pCountList[ i ];
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), nCount, pItem->GetCount() - nCount, pMixInfo->id, 0, 0, 0, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "TRANSMIT_ETHEREAL_DURABILITY", LOG::STR_NTS );
|
||
|
||
// 보호 대상 아이템이 아닌 경우만 재료 아이템 삭제
|
||
if( ( pMixInfo->value[ 1 ] & ( 1 << i ) ) == 0 )
|
||
pPlayer->EraseItem( pItem, nCount );
|
||
}
|
||
|
||
// 성공률 체크
|
||
bool bSuccess = XRandom( 0, 99 ) < pMixInfo->value[ 2 ];
|
||
const char * pszResult = ( bSuccess ) ? "SUCS" : "FAIL";
|
||
if( bSuccess )
|
||
{
|
||
__int64 nRecoverEtherealDurability = 0;
|
||
|
||
// 최대치 기준 % 회복량: ( pMixInfo->value[ 2 ] )%
|
||
assert( pMixInfo->value[ 2 ] >= 0 && pMixInfo->value[ 2 ] <= 100 );
|
||
nRecoverEtherealDurability = pMainMaterial->GetMaxEtherealDurability();
|
||
nRecoverEtherealDurability = nRecoverEtherealDurability * pMixInfo->value[ 2 ] / 100;
|
||
|
||
// 절대치 회복량: pMixInfo->value[ 0 ]
|
||
nRecoverEtherealDurability += pMixInfo->value[ 0 ];
|
||
|
||
// 오버플로우 체크
|
||
if( nRecoverEtherealDurability > INT_MAX )
|
||
{
|
||
assert( 0 );
|
||
nRecoverEtherealDurability = INT_MAX;
|
||
}
|
||
|
||
pMainMaterial->AddCurrentEtherealDurability( nRecoverEtherealDurability );
|
||
|
||
SendItemMessage( pPlayer, pMainMaterial );
|
||
PrintfChatMessage( false, CHAT_ITEM, "@SYSTEM", pPlayer, "@7901\v#@Ethereal_Durability@#\v%d", nRecoverEtherealDurability / 10000 );
|
||
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &tmp, 1 );
|
||
}
|
||
else
|
||
SendMixResult( pPlayer, NULL, 0 );
|
||
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, pszResult, LOG::STR_NTS );
|
||
|
||
return bSuccess;
|
||
}
|
||
|
||
bool MixManager::CreateItemByDecompose( struct StructPlayer * pPlayer, DecomposeBase & decomposeInfo, std::vector< std::pair< struct StructItem*, __int64 > > *vCreatedItem, int nCount )
|
||
{
|
||
// 분해 유형: 101- DECOMPOSE_CREATE_ITEM
|
||
// value[0]: 성공률, value[1]: 생성아이템ID, value[2]: 최소값, value[3]: 최대값
|
||
int nProbability = decomposeInfo.value[0] * 100 ;
|
||
|
||
for( int i = 0 ; i < nCount ; ++i )
|
||
{
|
||
if( XRandom( 1, 10000 ) <= nProbability )
|
||
{
|
||
int forCnt = 1;
|
||
int dropCnt = XRandom( decomposeInfo.value[2], decomposeInfo.value[3] );
|
||
|
||
// 드랍 그룹에서 선택 되면 forLoop를 itemCnt만큼 돈다.
|
||
// DecomposeInfo 내의 최소/최대 개수와 드랍Group내의 최소/최대 개수가 따로 존재한다는 데에 주의하자.
|
||
ItemBase::ItemCode dropID = decomposeInfo.value[1];
|
||
if( dropID < 0 )
|
||
{
|
||
forCnt = dropCnt;
|
||
}
|
||
|
||
for( int i = 0; i < forCnt; ++i )
|
||
{
|
||
ItemBase::ItemCode itemCode = dropID;
|
||
__int64 itemCnt = dropCnt;
|
||
|
||
while( itemCode < 0 ) // 드랍 그룹일 경우 아이템 코드 선택
|
||
{
|
||
GameContent::SelectItemIDFromDropGroup( itemCode, itemCode, itemCnt );
|
||
}
|
||
|
||
StructItem* pNewItem = StructItem::AllocItem( 0, itemCode, itemCnt, ItemInstance::BY_DECOMPOSE );
|
||
itemCnt = pNewItem->GetCount();
|
||
if( !pNewItem->IsJoinable() || !pPlayer->GetInventory()->Find( itemCode ) )
|
||
pNewItem = pPlayer->PushItem( pNewItem, itemCnt );
|
||
else
|
||
pNewItem = pPlayer->PushItem( pNewItem, itemCnt, true );
|
||
|
||
if( pNewItem )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_TAKE, pPlayer->GetAccountID(), pPlayer->GetSID(), pNewItem->GetItemEnhance() * 100 + pNewItem->GetItemLevel(), pNewItem->GetItemCode(), dropCnt, pNewItem->GetCount(), pPlayer->GetGold().GetRawData(), pPlayer->GetGold().GetRawData(), pPlayer->GetX(), pPlayer->GetY(), pNewItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "DECOMPOSE", LOG::STR_NTS );
|
||
LOG::Log11N4S( LM_ITEM_DECOMPOSE_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pNewItem->GetItemEnhance() * 100 + pNewItem->GetItemLevel(), itemCode, dropCnt, pNewItem->GetCount(), 0, 0, 0, decomposeInfo.id, pNewItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "SUCS", LOG::STR_NTS, "DECOMPOSE_CREATE_ITEM", LOG::STR_NTS );
|
||
|
||
bool bCheck = false;
|
||
for( std::vector< std::pair< struct StructItem*, __int64 > >::iterator it = vCreatedItem->begin() ; it != vCreatedItem->end() ; ++it )
|
||
{
|
||
if( it->first == pNewItem )
|
||
{
|
||
it->second += itemCnt;
|
||
bCheck = true;
|
||
break;
|
||
}
|
||
}
|
||
if( !bCheck )
|
||
vCreatedItem->push_back( std::make_pair( pNewItem, itemCnt ) );
|
||
}
|
||
else
|
||
{
|
||
_cprint( "Pushing item failed in decomposing: itemCode(%d), itemCnt(%d), player(%s)\n", itemCode, itemCnt, pPlayer->GetName() );
|
||
FILELOG( "Pushing item failed in decomposing: itemCode(%d), itemCnt(%d), player(%s)", itemCode, itemCnt, pPlayer->GetName() );
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_DECOMPOSE_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), 0, 0, 0, 0, 0, 0, 0, decomposeInfo.id, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "FAIL", LOG::STR_NTS, "DECOMPOSE_CREATE_ITEM", LOG::STR_NTS );
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
bool MixManager::InsertAwakenItem( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
if( pMainMaterial->IsAwaken() )
|
||
return false;
|
||
|
||
int arrRate[ 40 ] = {0, };
|
||
RandomManager::GetAwakenProbabilitiesInfo( arrRate, _countof( arrRate ) );
|
||
|
||
int nMinAwakenCount = pMixInfo->value[ 0 ];
|
||
int nMaxAwakenCount = pMixInfo->value[ 1 ];
|
||
|
||
ItemInstance::RANDOM_OPTION kAwakenOption;
|
||
kAwakenOption.nRandomType = ItemInstance::AWAKEN;
|
||
|
||
int nType = 0;
|
||
int nBitSet = 0;
|
||
int nAwakenData = 0;
|
||
int nAwakenCount = XRandom( nMinAwakenCount, nMaxAwakenCount );
|
||
|
||
if( nAwakenCount > ItemInstance::MAX_AWAKEN_NUMBER )
|
||
nAwakenCount = ItemInstance::MAX_AWAKEN_NUMBER;
|
||
|
||
for( int nOptionCnt = 0 ; nOptionCnt < nAwakenCount ; )
|
||
{
|
||
int nKey = XRandom( 1, 100000 );
|
||
bool bCheckResult = true;
|
||
|
||
for( int nIndex = 0 ; nIndex < 40 ; ++nIndex )
|
||
{
|
||
if( nKey > arrRate[ nIndex ] ) continue;
|
||
|
||
int nOptionType = RandomManager::GetAwakenValueType( pMainMaterial );
|
||
|
||
nAwakenData = RandomManager::GetAwakenInfoValue( nIndex, nOptionType, nBitSet, nType );
|
||
if( !nAwakenData ) return false;
|
||
|
||
if( !RandomManager::CheckMaxAwakenOption( &kAwakenOption, nBitSet, nType, nAwakenData ) )
|
||
{
|
||
bCheckResult = false;
|
||
}
|
||
break;
|
||
}
|
||
|
||
if( bCheckResult )
|
||
{
|
||
kAwakenOption.OptionInfo[ nOptionCnt ].nType = nType;
|
||
kAwakenOption.OptionInfo[ nOptionCnt ].fValue1 = nBitSet;
|
||
kAwakenOption.OptionInfo[ nOptionCnt ].fValue2 = nAwakenData;
|
||
|
||
++nOptionCnt;
|
||
}
|
||
}
|
||
|
||
// MainMaterial
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "AWAKEN_ITEM", LOG::STR_NTS );
|
||
|
||
kAwakenOption.nSID = RandomManager::AllocRandomSID();
|
||
if( !pMainMaterial->SetAwakenOption( kAwakenOption ) ) return false;
|
||
|
||
for( int nCnt = 0 ; nCnt < nSubMaterialCount ; ++nCnt )
|
||
{
|
||
// SubMaterial
|
||
struct StructItem *pItem = pSubMaterial[ nCnt ];
|
||
unsigned short nCount = pCountList[ nCnt ];
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), nCount, pItem->GetCount() - nCount, pMixInfo->id, 0, 0, 0, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "AWAKEN_ITEM", LOG::STR_NTS );
|
||
|
||
pPlayer->EraseItem( pSubMaterial[ nCnt ], pCountList[ nCnt ] );
|
||
}
|
||
|
||
pMainMaterial->DBQuery( new DB_UpdateItemAwakenSID( pMainMaterial ) );
|
||
pMainMaterial->DBQuery( new DB_SetRandomOption( pPlayer, pMainMaterial, kAwakenOption ) );
|
||
|
||
SendItemMessage( pPlayer, pMainMaterial );
|
||
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &tmp, 1, TS_SC_MIX_RESULT::MIX_TYPE_AWAKEN );
|
||
|
||
// Mix Result
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "", LOG::STR_NTS );
|
||
|
||
// Awaken Result
|
||
char szAwakenValue[128] = { 0, };
|
||
char szAwakenData[128] = { 0, };
|
||
s_itoa( pMainMaterial->GetAwakenOptionValue1( 4 ), szAwakenValue, _countof( szAwakenValue ), 10 );
|
||
s_itoa( pMainMaterial->GetAwakenOptionValue2( 4 ), szAwakenData, _countof( szAwakenData ), 10 );
|
||
LOG::Log11N4S( LM_INSERT_AWAKEN_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetAwakenOptionValue1(0), pMainMaterial->GetAwakenOptionValue2(0), pMainMaterial->GetAwakenOptionValue1(1), pMainMaterial->GetAwakenOptionValue2(1), pMainMaterial->GetAwakenOptionValue1(2), pMainMaterial->GetAwakenOptionValue2(2), pMainMaterial->GetAwakenOptionValue1(3), pMainMaterial->GetAwakenOptionValue2(3), pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, szAwakenValue, LOG::STR_NTS, szAwakenData, LOG::STR_NTS );
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
bool MixManager::DeleteAwakenOption( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
if( !pMainMaterial->IsAwaken() )
|
||
return false;
|
||
|
||
// MainMaterial
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "UNAWAKEN_ITEM", LOG::STR_NTS );
|
||
|
||
int nAwakenSID = pMainMaterial->GetAwakenSID();
|
||
|
||
ItemInstance::RANDOM_OPTION kDummyOption;
|
||
kDummyOption.nRandomType = ItemInstance::AWAKEN;
|
||
pMainMaterial->SetRandomOption( ItemInstance::AWAKEN, kDummyOption );
|
||
|
||
pMainMaterial->DBQuery( new DB_UpdateItemAwakenSID( pMainMaterial ) );
|
||
pMainMaterial->DBQuery( new DB_DeleteRandomOption( pMainMaterial, nAwakenSID ) );
|
||
|
||
for( int nCnt = 0 ; nCnt < nSubMaterialCount ; ++nCnt )
|
||
{
|
||
// SubMaterial
|
||
struct StructItem *pItem = pSubMaterial[ nCnt ];
|
||
unsigned short nCount = pCountList[ nCnt ];
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), nCount, pItem->GetCount() - nCount, pMixInfo->id, 0, 0, 0, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "UNAWAKEN_ITEM", LOG::STR_NTS );
|
||
|
||
pPlayer->EraseItem( pSubMaterial[nCnt], pCountList[nCnt] );
|
||
}
|
||
|
||
SendItemMessage( pPlayer, pMainMaterial );
|
||
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult( pPlayer, &tmp, 1, 1 );
|
||
|
||
// Mix Result
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
|
||
|
||
// Awaken Result
|
||
char szAwakenValue[128] = { 0, };
|
||
char szAwakenData[128] = { 0, };
|
||
s_itoa( pMainMaterial->GetAwakenOptionValue1( 4 ), szAwakenValue, _countof( szAwakenValue ), 10 );
|
||
s_itoa( pMainMaterial->GetAwakenOptionValue2( 4 ), szAwakenData, _countof( szAwakenData ), 10 );
|
||
LOG::Log11N4S( LM_DELETE_AWAKEN_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetAwakenOptionValue1(0), pMainMaterial->GetAwakenOptionValue2(0), pMainMaterial->GetAwakenOptionValue1(1), pMainMaterial->GetAwakenOptionValue2(1), pMainMaterial->GetAwakenOptionValue1(2), pMainMaterial->GetAwakenOptionValue2(2), pMainMaterial->GetAwakenOptionValue1(3), pMainMaterial->GetAwakenOptionValue2(3), pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, szAwakenValue, LOG::STR_NTS, szAwakenData, LOG::STR_NTS );
|
||
|
||
return true;
|
||
}
|
||
|
||
bool MixManager::IdentifyItemForRandomOption( const MixBase * pMixInfo, struct StructPlayer * pPlayer, struct StructItem * pMainMaterial, int nSubMaterialCount, struct StructItem ** pSubMaterial, unsigned short *pCountList, std::vector< std::pair< StructItem *, __int64 > > * vCreatedItem )
|
||
{
|
||
// 랜덤화 가능여부 조사
|
||
if( !pPlayer->IsRandomizable( pMainMaterial ) )
|
||
return false;
|
||
|
||
// 로그 남긴다.
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "IDENTIFY_ITEM", LOG::STR_NTS );
|
||
for( int i = 0; i < nSubMaterialCount; ++i )
|
||
{
|
||
LOG::Log11N4S( LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pSubMaterial[i]->GetItemEnhance() * 100 + pSubMaterial[i]->GetItemLevel(), pSubMaterial[i]->GetItemCode(), pCountList[i], pSubMaterial[i]->GetCount() - pCountList[i], pMixInfo->id, pSubMaterial[i]->GetCurrentEtherealDurability(), 0, 0, pSubMaterial[i]->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "IDENTIFY_ITEM", LOG::STR_NTS );
|
||
pPlayer->EraseItem( pSubMaterial[i], pCountList[i] );
|
||
}
|
||
|
||
// value[0] = 랜덤화 성공률, value[1] = 프리셋ID
|
||
int sucRate = pMixInfo->value[ 0 ] * 10000;
|
||
if( XRandom( 0, 1000000 ) <= sucRate )
|
||
{
|
||
int presetID = pMixInfo->value[ 1 ];
|
||
ItemInstance::RANDOM_OPTION kOption;
|
||
kOption.nSID = RandomManager::AllocRandomSID();
|
||
kOption.nRandomType = ItemInstance::IDENTIFIED;
|
||
if( !RandomManager::PickRandomOptionByPreset( &kOption, presetID ) )
|
||
return false;
|
||
pMainMaterial->SetIdentifiedOption( kOption );
|
||
int nLogCnt = 0;
|
||
for( ; nLogCnt < ItemInstance::MAX_RANDOM_OPTION_NUMBER ; ++nLogCnt )
|
||
{
|
||
if( !kOption.OptionInfo[ nLogCnt ].nType )
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 랜덤 옵션 두개마다 로그 하나씩 기록. (실제 옵션 값은 소수점도 기록하기 위해 한자리 올려서 기록한다. 예: 15.6->156 )
|
||
for( int i = 0 ; i < nLogCnt ; i+=2 )
|
||
{
|
||
LOG::Log11N4S( LM_INSERT_RANDOM_OPTION, pPlayer->GetAccountID(), pPlayer->GetSID(), kOption.OptionInfo[ i ].nType, kOption.OptionInfo[ i ].fValue1, kOption.OptionInfo[ i ].fValue2.get()/1000, kOption.OptionInfo[ i + 1 ].nType, kOption.OptionInfo[ i + 1 ].fValue1, kOption.OptionInfo[ i + 1 ].fValue2.get()/1000, pMainMaterial->GetItemCode(), pMixInfo->value[ 1 ], pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", LOG::STR_NTS, "ByMix", LOG::STR_NTS );
|
||
}
|
||
|
||
// DB업뎃
|
||
pMainMaterial->DBQuery( new DB_UpdateItemRandomOptionSID( pMainMaterial ) );
|
||
pMainMaterial->DBQuery( new DB_SetRandomOption( pPlayer, pMainMaterial, kOption ) );
|
||
|
||
AR_HANDLE handle = pMainMaterial->GetHandle();
|
||
SendItemMessage( pPlayer, pMainMaterial );
|
||
SendMixResult( pPlayer, &handle, 1, TS_SC_MIX_RESULT::MIX_TYPE_RANDOM_OPTION );
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "SUCS", 0, "", LOG::STR_NTS );
|
||
}
|
||
else
|
||
{
|
||
//실패처리
|
||
SendMixResult( pPlayer, NULL, 0, TS_SC_MIX_RESULT::MIX_TYPE_RANDOM_OPTION );
|
||
LOG::Log11N4S( LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "FAIL", 0, "", LOG::STR_NTS );
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
bool MixManager::ReAwakenOption(const MixBase* pMixInfo, struct StructPlayer* pPlayer, struct StructItem* pMainMaterial, int nSubMaterialCount, struct StructItem** pSubMaterial, unsigned short* pCountList, std::vector< std::pair< StructItem*, __int64 > >* vCreatedItem)
|
||
{
|
||
if (!pMainMaterial->IsAwaken())
|
||
return false;
|
||
|
||
int arrRate[40] = { 0, };
|
||
RandomManager::GetAwakenProbabilitiesInfo(arrRate, _countof(arrRate));
|
||
|
||
int nMinAwakenCount = pMixInfo->value[0];
|
||
int nMaxAwakenCount = pMixInfo->value[1];
|
||
|
||
ItemInstance::RANDOM_OPTION kAwakenOption;
|
||
kAwakenOption.nRandomType = ItemInstance::AWAKEN;
|
||
|
||
int nType = 0;
|
||
int nBitSet = 0;
|
||
int nAwakenData = 0;
|
||
int nAwakenCount = XRandom(nMinAwakenCount, nMaxAwakenCount);
|
||
|
||
if (nAwakenCount > ItemInstance::MAX_AWAKEN_NUMBER)
|
||
nAwakenCount = ItemInstance::MAX_AWAKEN_NUMBER;
|
||
|
||
for (int nOptionCnt = 0; nOptionCnt < nAwakenCount; )
|
||
{
|
||
int nKey = XRandom(1, 100000);
|
||
bool bCheckResult = true;
|
||
|
||
for (int nIndex = 0; nIndex < 40; ++nIndex)
|
||
{
|
||
if (nKey > arrRate[nIndex]) continue;
|
||
|
||
int nOptionType = RandomManager::GetAwakenValueType(pMainMaterial);
|
||
|
||
nAwakenData = RandomManager::GetAwakenInfoValue(nIndex, nOptionType, nBitSet, nType);
|
||
if (!nAwakenData) return false;
|
||
|
||
if (!RandomManager::CheckMaxAwakenOption(&kAwakenOption, nBitSet, nType, nAwakenData))
|
||
{
|
||
bCheckResult = false;
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (bCheckResult)
|
||
{
|
||
kAwakenOption.OptionInfo[nOptionCnt].nType = nType;
|
||
kAwakenOption.OptionInfo[nOptionCnt].fValue1 = nBitSet;
|
||
kAwakenOption.OptionInfo[nOptionCnt].fValue2 = nAwakenData;
|
||
|
||
++nOptionCnt;
|
||
}
|
||
}
|
||
|
||
// MainMaterial
|
||
LOG::Log11N4S(LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "AWAKEN_ITEM", LOG::STR_NTS);
|
||
|
||
kAwakenOption.nSID = pMainMaterial->GetAwakenSID() ;
|
||
if (!pMainMaterial->SetAwakenOption(kAwakenOption)) return false;
|
||
|
||
for (int nCnt = 0; nCnt < nSubMaterialCount; ++nCnt)
|
||
{
|
||
// SubMaterial
|
||
struct StructItem* pItem = pSubMaterial[nCnt];
|
||
unsigned short nCount = pCountList[nCnt];
|
||
LOG::Log11N4S(LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), nCount, pItem->GetCount() - nCount, pMixInfo->id, 0, 0, 0, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "AWAKEN_ITEM", LOG::STR_NTS);
|
||
|
||
pPlayer->EraseItem(pSubMaterial[nCnt], pCountList[nCnt]);
|
||
}
|
||
|
||
pMainMaterial->DBQuery(new DB_UpdateItemAwakenSID(pMainMaterial));
|
||
pMainMaterial->DBQuery(new DB_SetRandomOption(pPlayer, pMainMaterial, kAwakenOption));
|
||
|
||
AR_HANDLE handle = pMainMaterial->GetHandle();
|
||
|
||
SendItemMessage(pPlayer, pMainMaterial);
|
||
SendMixResult(pPlayer, &handle, 1);
|
||
|
||
std::string strFirstSummonHandler;
|
||
XStringUtil::Format(strFirstSummonHandler, "reawaken_log( %d )", handle);
|
||
LUA()->RunString(strFirstSummonHandler.c_str());
|
||
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
bool MixManager::DeleteRandomOption(const MixBase* pMixInfo, struct StructPlayer* pPlayer, struct StructItem* pMainMaterial, int nSubMaterialCount, struct StructItem** pSubMaterial, unsigned short* pCountList, std::vector< std::pair< StructItem*, __int64 > >* vCreatedItem)
|
||
{
|
||
if (!pMainMaterial->IsIdentified())
|
||
return false;
|
||
|
||
int nAwakenSID = pMainMaterial->GetIdentifiedSID();
|
||
|
||
ItemInstance::RANDOM_OPTION kDummyOption;
|
||
kDummyOption.nRandomType = ItemInstance::IDENTIFIED;
|
||
pMainMaterial->SetRandomOption(ItemInstance::IDENTIFIED, kDummyOption);
|
||
|
||
pMainMaterial->DBQuery(new DB_UpdateItemRandomOptionSID(pMainMaterial));
|
||
pMainMaterial->DBQuery(new DB_DeleteRandomOption(pMainMaterial, nAwakenSID));
|
||
|
||
for (int nCnt = 0; nCnt < nSubMaterialCount; ++nCnt)
|
||
{
|
||
// SubMaterial
|
||
struct StructItem* pItem = pSubMaterial[nCnt];
|
||
unsigned short nCount = pCountList[nCnt];
|
||
pPlayer->EraseItem(pSubMaterial[nCnt], pCountList[nCnt]);
|
||
}
|
||
|
||
AR_HANDLE handle = pMainMaterial->GetHandle();
|
||
SendItemMessage(pPlayer, pMainMaterial);
|
||
SendMixResult(pPlayer, &handle, 1, TS_SC_MIX_RESULT::MIX_TYPE_RANDOM_OPTION);
|
||
|
||
return true;
|
||
}
|
||
|
||
bool MixManager::ReIdentifyItemForRandomOption(const MixBase* pMixInfo, struct StructPlayer* pPlayer, struct StructItem* pMainMaterial, int nSubMaterialCount, struct StructItem** pSubMaterial, unsigned short* pCountList, std::vector< std::pair< StructItem*, __int64 > >* vCreatedItem)
|
||
{
|
||
if (!pMainMaterial->IsIdentified())
|
||
return false;
|
||
|
||
//Delete Identification
|
||
ItemInstance::RANDOM_OPTION kOption;
|
||
kOption.nSID = pMainMaterial->GetIdentifiedSID();
|
||
kOption.nRandomType = ItemInstance::IDENTIFIED;
|
||
|
||
|
||
// Identification
|
||
|
||
for (int i = 0; i < nSubMaterialCount; ++i)
|
||
{
|
||
pPlayer->EraseItem(pSubMaterial[i], pCountList[i]);
|
||
}
|
||
|
||
int sucRate = pMixInfo->value[0] * 10000;
|
||
if (XRandom(0, 1000000) <= sucRate)
|
||
{
|
||
int presetID = pMixInfo->value[1];
|
||
|
||
if (!RandomManager::PickRandomOptionByPreset(&kOption, presetID ))
|
||
return false;
|
||
|
||
|
||
pMainMaterial->SetIdentifiedOption(kOption);
|
||
|
||
pMainMaterial->DBQuery(new DB_SetRandomOption(pPlayer, pMainMaterial, kOption));
|
||
pPlayer->onAfterChangeItemProperty(pMainMaterial);
|
||
|
||
AR_HANDLE handle = pMainMaterial->GetHandle();
|
||
SendItemMessage(pPlayer, pMainMaterial);
|
||
SendMixResult(pPlayer, &handle, 1, TS_SC_MIX_RESULT::MIX_TYPE_RANDOM_OPTION);
|
||
|
||
}
|
||
else
|
||
{
|
||
SendMixResult(pPlayer, NULL, 0, TS_SC_MIX_RESULT::MIX_TYPE_RANDOM_OPTION);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
bool MixManager::rdmSocketRandomOption(const MixBase* pMixInfo, struct StructPlayer* pPlayer, struct StructItem* pMainMaterial, int nSubMaterialCount, struct StructItem** pSubMaterial, unsigned short* pCountList, std::vector< std::pair< StructItem*, __int64 > >* vCreatedItem)
|
||
{
|
||
if (!pMainMaterial->IsIdentified())
|
||
return false;
|
||
|
||
std::string strResult;
|
||
AR_HANDLE handle = pMainMaterial->GetHandle();
|
||
std::string strFirstSummonHandler;
|
||
XStringUtil::Format(strFirstSummonHandler, "return rdm_socket( %d )", handle);
|
||
LUA()->RunString(strFirstSummonHandler.c_str() , &strResult);
|
||
|
||
if (strResult != "true")
|
||
return false;
|
||
|
||
for (int nCnt = 0; nCnt < nSubMaterialCount; ++nCnt)
|
||
{
|
||
// SubMaterial
|
||
struct StructItem* pItem = pSubMaterial[nCnt];
|
||
unsigned short nCount = pCountList[nCnt];
|
||
pPlayer->EraseItem(pSubMaterial[nCnt], pCountList[nCnt]);
|
||
}
|
||
|
||
|
||
|
||
SendItemMessage(pPlayer, pMainMaterial);
|
||
SendMixResult(pPlayer, &handle, 1, TS_SC_MIX_RESULT::MIX_TYPE_RANDOM_OPTION);
|
||
|
||
return true;
|
||
}
|
||
|
||
bool MixManager::MixWithLuaScript(const MixBase* pMixInfo, struct StructPlayer* pPlayer, struct StructItem* pMainMaterial, int nSubMaterialCount, struct StructItem** pSubMaterial, unsigned short* pCountList, std::vector< std::pair< StructItem*, __int64 > >* vCreatedItem)
|
||
{
|
||
|
||
std::string strResult;
|
||
AR_HANDLE handle = pMainMaterial->GetHandle();
|
||
std::string strFirstSummonHandler;
|
||
XStringUtil::Format(strFirstSummonHandler, "return MixWithLuaScript( %d , %d )", handle, pMixInfo->value[0]);
|
||
LUA()->RunString(strFirstSummonHandler.c_str(), &strResult);
|
||
|
||
if (strResult != "true")
|
||
return false;
|
||
|
||
for (int nCnt = 0; nCnt < nSubMaterialCount; ++nCnt)
|
||
{
|
||
// SubMaterial
|
||
struct StructItem* pItem = pSubMaterial[nCnt];
|
||
unsigned short nCount = pCountList[nCnt];
|
||
pPlayer->EraseItem(pSubMaterial[nCnt], pCountList[nCnt]);
|
||
}
|
||
|
||
|
||
|
||
SendItemMessage(pPlayer, pMainMaterial);
|
||
SendMixResult(pPlayer, &handle, 1, TS_SC_MIX_RESULT::MIX_TYPE_RANDOM_OPTION);
|
||
|
||
return true;
|
||
}
|
||
|
||
bool MixManager::InsertAwakenItembyRandomOption(const MixBase* pMixInfo, struct StructPlayer* pPlayer, struct StructItem* pMainMaterial, int nSubMaterialCount, struct StructItem** pSubMaterial, unsigned short* pCountList, std::vector< std::pair< StructItem*, __int64 > >* vCreatedItem)
|
||
{
|
||
if (pMainMaterial->IsAwaken())
|
||
return false;
|
||
|
||
|
||
for (int i = 0; i < nSubMaterialCount; ++i)
|
||
{
|
||
pPlayer->EraseItem(pSubMaterial[i], pCountList[i]);
|
||
}
|
||
|
||
int sucRate = pMixInfo->value[0] * 10000;
|
||
if (XRandom(0, 1000000) <= sucRate)
|
||
{
|
||
|
||
int presetID = pMixInfo->value[1];
|
||
ItemInstance::RANDOM_OPTION kOption;
|
||
kOption.nSID = RandomManager::AllocRandomSID();
|
||
kOption.nRandomType = ItemInstance::AWAKEN;
|
||
if (!RandomManager::PickAwakenRandomOptionByPreset(&kOption, presetID))
|
||
return false;
|
||
pMainMaterial->SetAwakenOption(kOption);
|
||
int nLogCnt = 0;
|
||
for (; nLogCnt < ItemInstance::MAX_RANDOM_OPTION_NUMBER; ++nLogCnt)
|
||
{
|
||
if (!kOption.OptionInfo[nLogCnt].nType)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
pMainMaterial->DBQuery(new DB_UpdateItemAwakenSID(pMainMaterial));
|
||
pMainMaterial->DBQuery(new DB_SetRandomOption(pPlayer, pMainMaterial, kOption));
|
||
|
||
AR_HANDLE handle = pMainMaterial->GetHandle();
|
||
SendItemMessage(pPlayer, pMainMaterial);
|
||
SendMixResult(pPlayer, &handle, 1, TS_SC_MIX_RESULT::MIX_TYPE_AWAKEN);
|
||
|
||
std::string strFirstSummonHandler;
|
||
XStringUtil::Format(strFirstSummonHandler, "reawaken_log( %d )", handle );
|
||
LUA()->RunString(strFirstSummonHandler.c_str());
|
||
|
||
}
|
||
else
|
||
{
|
||
SendMixResult(pPlayer, NULL, 0, TS_SC_MIX_RESULT::MIX_TYPE_AWAKEN);
|
||
}
|
||
|
||
|
||
|
||
|
||
return true;
|
||
|
||
|
||
|
||
}
|
||
|
||
bool MixManager::ReAwakenItembyRandomOption(const MixBase* pMixInfo, struct StructPlayer* pPlayer, struct StructItem* pMainMaterial, int nSubMaterialCount, struct StructItem** pSubMaterial, unsigned short* pCountList, std::vector< std::pair< StructItem*, __int64 > >* vCreatedItem)
|
||
{
|
||
if (!pMainMaterial->IsAwaken())
|
||
return false;
|
||
|
||
for (int nCnt = 0; nCnt < nSubMaterialCount; ++nCnt)
|
||
{
|
||
// SubMaterial
|
||
struct StructItem* pItem = pSubMaterial[nCnt];
|
||
unsigned short nCount = pCountList[nCnt];
|
||
LOG::Log11N4S(LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), nCount, pItem->GetCount() - nCount, pMixInfo->id, 0, 0, 0, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "AWAKEN_ITEM", LOG::STR_NTS);
|
||
|
||
pPlayer->EraseItem(pSubMaterial[nCnt], pCountList[nCnt]);
|
||
}
|
||
|
||
|
||
int sucRate = pMixInfo->value[0] * 10000;
|
||
if (XRandom(0, 1000000) <= sucRate)
|
||
{
|
||
int presetID = pMixInfo->value[1];
|
||
ItemInstance::RANDOM_OPTION kOption;
|
||
kOption.nSID = pMainMaterial->GetAwakenSID();
|
||
kOption.nRandomType = ItemInstance::AWAKEN;
|
||
if (!RandomManager::PickAwakenRandomOptionByPreset(&kOption, presetID))
|
||
return false;
|
||
pMainMaterial->SetAwakenOption(kOption);
|
||
|
||
pMainMaterial->DBQuery(new DB_SetRandomOption(pPlayer, pMainMaterial, kOption));
|
||
|
||
AR_HANDLE handle = pMainMaterial->GetHandle();
|
||
|
||
SendItemMessage(pPlayer, pMainMaterial);
|
||
SendMixResult(pPlayer, &handle, 1);
|
||
//SendMixResult(pPlayer, &handle, 1, TS_SC_MIX_RESULT::MIX_TYPE_RANDOM_OPTION);
|
||
|
||
std::string strFirstSummonHandler;
|
||
XStringUtil::Format(strFirstSummonHandler, "reawaken_log( %d )", handle);
|
||
LUA()->RunString(strFirstSummonHandler.c_str());
|
||
|
||
}
|
||
else
|
||
{
|
||
SendMixResult(pPlayer, NULL, 0, TS_SC_MIX_RESULT::MIX_TYPE_AWAKEN);
|
||
}
|
||
|
||
|
||
return true;
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
// Fraun Sky Accessories 7/12/2025
|
||
bool MixManager::ChangeAdditionalItemEffect(const MixBase* pMixInfo, struct StructPlayer* pPlayer, struct StructItem* pMainMaterial, int nSubMaterialCount, struct StructItem** pSubMaterial, unsigned short* pCountList, std::vector< std::pair< StructItem*, __int64 > >* vCreatedItem)
|
||
{
|
||
|
||
if (pMixInfo->type != 610)
|
||
{
|
||
return false; // Idk why, but I think it worth it
|
||
}
|
||
|
||
|
||
|
||
LOG::Log11N4S(LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "CHANGE_ITEMEFFECT", LOG::STR_NTS);
|
||
for (int i = 0; i < nSubMaterialCount; ++i)
|
||
{
|
||
StructItem* pItem = pSubMaterial[i];
|
||
unsigned short nCount = pCountList[i];
|
||
|
||
LOG::Log11N4S(LM_ITEM_MIX_TRY, pPlayer->GetAccountID(), pPlayer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), nCount, pItem->GetCount() - nCount, pMixInfo->id, pItem->GetCurrentEtherealDurability(), 0, 0, pItem->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "CHANGE_ITEMEFFECT", LOG::STR_NTS);
|
||
|
||
pPlayer->EraseItem(pItem, nCount);
|
||
}
|
||
|
||
int nRandom = pMixInfo->value[0] * 100;
|
||
|
||
bool bResult = false;
|
||
const char* szRes = "FAIL";
|
||
|
||
if (XRandom(1, 10000) <= nRandom)
|
||
{
|
||
int nItemCode = pMainMaterial->GetItemCode();
|
||
int nResultEffectID = pMixInfo->value[1];
|
||
|
||
|
||
|
||
// Currently, failure cases do not occur, but in case setting the value fails in the future
|
||
// We perform a check to ensure that materials are not lost
|
||
if (!pMainMaterial->ChangeAdditionalItemEffect(nItemCode, nResultEffectID))
|
||
{
|
||
SendResult(pPlayer, pMixInfo->id, RESULT_INVALID_ARGUMENT);
|
||
return false;
|
||
}
|
||
|
||
// Updating is required to avoid bullshit
|
||
pMainMaterial = pPlayer->onAfterChangeItemProperty(pMainMaterial);
|
||
|
||
AR_HANDLE tmp = pMainMaterial->GetHandle();
|
||
SendMixResult(pPlayer, &tmp, 1);
|
||
|
||
szRes = "SUCS";
|
||
bResult = true;
|
||
|
||
// Log and remove material items
|
||
LOG::Log11N4S(LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS);
|
||
}
|
||
else
|
||
{
|
||
LOG::Log11N4S(LM_ITEM_MIX_RESULT, pPlayer->GetAccountID(), pPlayer->GetSID(), pMainMaterial->GetItemEnhance() * 100 + pMainMaterial->GetItemLevel(), pMainMaterial->GetItemCode(), 1, pMainMaterial->GetCount(), pMixInfo->id, pMainMaterial->GetCurrentEtherealDurability(), 0, 0, pMainMaterial->GetItemUID(), pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, szRes, LOG::STR_NTS);
|
||
|
||
SendMixResult(pPlayer, NULL, 0);
|
||
}
|
||
|
||
return bResult;
|
||
} |