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

3188 lines
140 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <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, theres 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;
}