Files
2026-06-01 12:46:52 +02:00

975 lines
29 KiB
C++

#include <network/XIOCPConnection.h>
#include <mmo/ArcadiaServer.h>
#include <toolkit/XRandom.h>
#include <logging/FileLog.h>
#include <toolkit/XStringUtil.h>
#include <toolkit/XQuadScanner.h>
#include <toolkit/XEnv.h>
#include "ErrorCode/ErrorCode.h"
#include "LogClient/LogClient.h"
#include "GameProc.h"
#include "StructItem.h"
#include "StructMonster.h"
#include "StructSummon.h"
#include "StructPet.h"
#include "StructNPC.h"
#include "StructPlayer.h"
#include "StructFieldProp.h"
#include "StructSkill.h"
#include "GameContent.h"
#include "ItemCollector.h"
#include "DB_Commands.h"
#include "GameMessage.h"
#include "LuaVM.h"
#include "SendMessage.h"
#include "Extern.h"
#include "ChannelManager.h"
#include "DungeonManager.h"
#include "StructWorldLocation.h"
#include "PartyManager.h"
// 테이밍 세팅
bool SetTamer( struct StructMonster *pMonster, struct StructPlayer *pPlayer, struct StructSkill *pSkill )
{
if( pPlayer->GetTamingTarget() ) return false;
const ItemBase::ItemCode BASIC_CREATURE_CARD = 0;
const ItemBase::ItemCode RARE_CREATURE_CARD = 0;
const ItemBase::ItemCode COMMON_CREATURE_CARD = 0;
const ItemBase::ItemCode UNCOMMON_CREATURE_CARD = 0;
// HP 100%인지 체크
if( pMonster->GetHP() != pMonster->GetMaxHP() ) return false;
// 이미 테이밍 되어 있으면 KIN
if( pMonster->GetTamer() ) return false;
// 플레이어가 아니라면 KIN
if( !pPlayer->IsPlayer() ) return false;
// 이미 다른 몬스터를 테이밍 중일 경우 테이밍 불가
AR_HANDLE hTamingTarget = pPlayer->GetTamingTarget();
if( hTamingTarget ) return false;
// 테이밍 안되는 녀석
ItemBase::ItemCode hCardCode = pMonster->GetTameItemCode();
if( !hCardCode ) return false;
// 테이밍 카드를 가지고 있는지 조사
StructItem *pItem = pPlayer->FindEmptySummonCard( hCardCode );
if( !pItem ) return false;
// 세팅
pMonster->SetTamer( pPlayer->GetHandle(), pSkill );
pPlayer->SetTamingTarget( pMonster->GetHandle() );
pItem->SetInstanceFlagOn( ItemInstance::ITEM_FLAG_TAMING );
// 방송
BroadcastTamingMessage( pPlayer, pMonster, TS_SC_TAMING_INFO::START );
return true;
}
// 테이밍 풀림
void ClearTamer( struct StructMonster *pMonster, bool bBroadcastMsg )
{
if( !pMonster->GetTamer() ) return;
StructPlayer::iterator it = StructPlayer::get( pMonster->GetTamer() );
if( (*it) )
{
if( bBroadcastMsg )
BroadcastTamingMessage( (*it), pMonster, TS_SC_TAMING_INFO::CLEAR );
(*it)->SetTamingTarget( 0 );
// 테이밍 중이던 소환수 카드의 테이밍 상태 해제
StructItem *pItem = NULL;
while( pItem = (*it)->FindTamingSummonCard( pMonster->GetTameItemCode() ) )
{
pItem->SetInstanceFlagOff( ItemInstance::ITEM_FLAG_TAMING );
}
}
pMonster->SetTamer( 0, NULL );
}
// 테이밍
bool ProcTame( StructMonster *pMonster )
{
// 테이밍 된놈이 아님
if( !pMonster->GetTamer() ) return false;
StructPlayer::iterator it = StructPlayer::get( pMonster->GetTamer() );
StructPlayer *pTamer = *it;
// 테이머가 존재하지 않음
if( !pTamer || pTamer->IsDead() )
{
BroadcastTamingMessage( NULL, pMonster, TS_SC_TAMING_INFO::FAILED );
return false;
}
// 너무 멀다면 테이밍 실패
if( pMonster->GetPos().GetDistance( pTamer->GetPos() ) > RANGE_LIMIT )
{
ClearTamer( pMonster, false );
BroadcastTamingMessage( pTamer, pMonster, TS_SC_TAMING_INFO::FAILED );
return false;
}
// 테이밍 안되는 녀석
ItemBase::ItemCode nTameItemCode = pMonster->GetTameItemCode();
if( !nTameItemCode )
{
ClearTamer( pMonster, false );
BroadcastTamingMessage( pTamer, pMonster, TS_SC_TAMING_INFO::FAILED );
return false;
}
// 테이밍 카드를 가지고 있는지 조사
StructItem *pItem = pTamer->FindTamingSummonCard( nTameItemCode );
if( !pItem )
{
_cprint( "ProcTame: A summon card used for taming is lost. [%s:%s]\n", pTamer->GetAccountName(), pTamer->GetName() );
FILELOG( "ProcTame: A summon card used for taming is lost. [%s:%s]", pTamer->GetAccountName(), pTamer->GetName() );
ClearTamer( pMonster, false );
BroadcastTamingMessage( pTamer, pMonster, TS_SC_TAMING_INFO::FAILED );
return false;
}
const StructSkill *pSkill = pMonster->GetTamingSkill();
if( !pSkill )
{
assert( 0 );
ClearTamer( pMonster, false );
BroadcastTamingMessage( pTamer, pMonster, TS_SC_TAMING_INFO::FAILED );
return false;
}
bool bCardProtected = false;
StructItem * pMirrorOfTamingCard = NULL;
// 소모 순서 기준에 따라 순서대로 아이템 코드별 보유템 검색
( ( pMirrorOfTamingCard = pTamer->FindItem( (ItemBase::ItemCode) ItemBase::ITEM_CODE_MIRROR_OF_TAMING_CARD_ON_TEST ) ) ||
( pMirrorOfTamingCard = pTamer->FindItem( (ItemBase::ItemCode) ItemBase::ITEM_CODE_MIRROR_OF_TAMING_CARD ) ) ||
( pMirrorOfTamingCard = pTamer->FindItem( (ItemBase::ItemCode) ItemBase::ITEM_CODE_MIRROR_OF_TAMING_CARD_TRADABLE ) ) );
// 거울을 소지하고 있는 경우(만일을 대비한 체크를 함께 진행)
if( pMirrorOfTamingCard && pMirrorOfTamingCard->IsInInventory() && pMirrorOfTamingCard->GetOwnerHandle() == pTamer->GetHandle() && pMirrorOfTamingCard->GetCount() >= 1 )
{
const ItemUID nItemUID = pMirrorOfTamingCard->GetItemUID();
const __int64 nItemEnhanceAndLevel = pMirrorOfTamingCard->GetItemEnhance() * 100 + pMirrorOfTamingCard->GetItemLevel();
const ItemBase::ItemCode nItemCode = pMirrorOfTamingCard->GetItemCode();
const __int64 nItemCount = pMirrorOfTamingCard->GetCount();
const bool bDecreaseCount = pMirrorOfTamingCard->GetItemBase().nType != ItemBase::TYPE_USE;
// 성공이든 실패든 일단 거울은 박살남(박살 성공 여부에 따라 보호 여부를 최종적으로 결정)
if( pTamer->EraseItem( pMirrorOfTamingCard, 1 ) )
{
bCardProtected = true;
LOG::Log11N4S( LM_ITEM_USE, pTamer->GetAccountID(), pTamer->GetSID(), nItemEnhanceAndLevel, nItemCode, 1, ( bDecreaseCount ) ? nItemCount - 1 : nItemCount, 0, 0, 0, 0, nItemUID, pTamer->GetAccountName(), LOG::STR_NTS, pTamer->GetName(), LOG::STR_NTS, "", 0, "", 0 );
SendChatMessage( false, CHAT_ITEM, "@SYSTEM", pTamer, "@243" );
}
}
// 확률 계산
c_fixed10 fTameProbability = pMonster->GetTamePercetage();
fTameProbability *= ( pSkill->GetVar( 0 ) * pMonster->GetTamingSkillLevel() + pSkill->GetVar( 1 ) * pSkill->GetEnhance() + 1 ) * 100;
// Fraun chat message with % of taming
if (pTamer && ENV().GetInt("game.show_taming_percentage", 1))
{
// 520002 Taming chance: #@chance@# %
PrintfChatMessage(false, CHAT_NORMAL, "@SYSTEM", pTamer, "@520002\v#@chance@#\v%d", static_cast<int>(fTameProbability));
}
// 소수 자리 전체까지 확률 처리가 가능하도록 c_fixed10::get 함수를 사용한 값에 대해 랜덤 값 범위에 c_fixed10::FACTOR를 곱한 값을 비교
if( fTameProbability.get() < static_cast< int >( XRandom( 1, 1000000 ) ) )
{
LOG::Log11N4S( LM_CHARACTER_TAMING, pTamer->GetAccountID(), pTamer->GetSID(), 0, nTameItemCode, pMonster->GetMonsterId(), pItem->GetCount(), (bCardProtected)?pItem->GetCount():pItem->GetCount()-1, 0, pTamer->GetX(), pTamer->GetY(), pItem->GetItemUID(), pTamer->GetAccountName(), LOG::STR_NTS, pTamer->GetName(), LOG::STR_NTS, "", 0, "FAIL", LOG::STR_NTS );
if( !bCardProtected )
pTamer->EraseItem( pItem, 1 );
ClearTamer( pMonster, false );
BroadcastTamingMessage( pTamer, pMonster, TS_SC_TAMING_INFO::FAILED );
if( pMonster->GetTameCode() )
{
pTamer->UpdateTitleConditionBySummonTame( pMonster->GetTameCode(), GameContent::GetSummonInfo( pMonster->GetTameCode() )->rate, false );
}
return false;
}
// nSummonCode는 크리처의 종류 (기존 or 몬스터 크리처) 에 따라 다른 루틴에 의해 결정된다.
int nSummonCode = ( pMonster->IsMonsterCreatureTame() )? pMonster->GetMonsterCreatureTameCode() : pMonster->GetTameCode();
int nCode = pItem->GetItemCode();
LOG::Log11N4S( LM_CHARACTER_TAMING, pTamer->GetAccountID(), pTamer->GetSID(), 0, nTameItemCode, pMonster->GetMonsterId(), pItem->GetCount(), pItem->GetCount()-1, 0, pTamer->GetX(), pTamer->GetY(), pItem->GetItemUID(), pTamer->GetAccountName(), LOG::STR_NTS, pTamer->GetName(), LOG::STR_NTS, "", 0, "SUCS", LOG::STR_NTS );
// 성공을 했으면 빈카를 지우고 봉카를 넣어준다. (봉인 되면 중첩불가능이 되므로 새로 넣어줘야 한다.)
pTamer->EraseItem( pItem, 1 );
pItem = StructItem::AllocItem( 0, nCode, 1, ItemInstance::BY_TAMING );
pItem->SetInstanceFlagOn( ItemInstance::ITEM_FLAG_SUMMON );
pItem->SetSummonCode( nSummonCode );
StructItem *pNewCard = pTamer->PushItem( pItem, pItem->GetCount() );
if( pNewCard )
{
LOG::Log11N4S( LM_ITEM_TAKE, pTamer->GetAccountID(), pTamer->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), pItem->GetCount(), pNewCard->GetCount(), 0, pTamer->GetGold().GetRawData(), pTamer->GetX(), pTamer->GetY(), pItem->GetItemUID(), pTamer->GetAccountName(), LOG::STR_NTS, pTamer->GetName(), LOG::STR_NTS, "", 0, "TAMING", LOG::STR_NTS );
}
int summonCode = pItem->GetSummonCode();
if( pNewCard != pItem )
{
StructItem::PendFreeItem( pItem );
}
// 정보 보냄
SendItemMessage( pTamer, pItem );
BroadcastTamingMessage( pTamer, pMonster, TS_SC_TAMING_INFO::SUCCESS );
ClearTamer( pMonster, false );
pTamer->UpdateTitleConditionBySummonTame( nSummonCode, GameContent::GetSummonInfo( nSummonCode )->rate, true );
return true;
}
StructNPC *g_pCurrentNPC;
struct StructNPC * GetCurrentInitializingNPC()
{
return g_pCurrentNPC;
}
// 월드에서 Player를 제거
void RemovePlayerFromWorld( struct StructPlayer * pPlayer )
{
pPlayer->onRemoveFromWorld();
ArcadiaServer::Instance().RemoveObject( pPlayer );
}
// 월드에 NPC를 추가
void AddNPCToWorld( struct StructNPC * pNPC )
{
ArcadiaServer::Instance().AddObject( pNPC );
g_pCurrentNPC = pNPC;
}
// 월드에서 NPC를 제거
void RemoveNPCFromWorld( struct StructNPC * pMob )
{
pMob->onRemoveFromWorld();
ArcadiaServer::Instance().RemoveObject( pMob );
}
// Drop an item into the world
void AddItemToWorld( struct StructItem * pItem )
{
ArcadiaServer::Instance().AddObject( pItem );
pItem->SetDropTime( GetArTime() );
ItemCollector::Instance().RegisterItem( pItem );
}
// 월드에 필드프랍을 생성
void AddFieldPropToWorld( struct StructFieldProp * pProp )
{
ArcadiaServer::Instance().AddObject( pProp );
}
// Monster drops an item into the world
void MonsterDropItemToWorld( struct StructCreature *pCreature, struct StructItem * pItem )
{
TS_SC_ITEM_DROP_INFO info;
info.monster_handle = pCreature->GetHandle();
info.item_handle = pItem->GetHandle();
ArcadiaServer::Instance().Broadcast( pItem->GetRX(), pItem->GetRY(), pItem->GetLayer(), &info );
AddItemToWorld( pItem );
}
// 월드에서 아이템을 제거함
const bool RemoveItemFromWorld( struct StructItem * pItem )
{
if( !ItemCollector::Instance().UnregisterItem( pItem ) )
return false;
ArcadiaServer::Instance().RemoveObject( pItem );
pItem->SetDropTime( 0 );
return true;
}
// 월드에 몬스터를 추가
void AddMonsterToWorld( struct StructMonster * pMob )
{
pMob->quadTreeItem.Set( *pMob );
pMob->quadTreeItem.AddMe();
pMob->SetReturnPosition( pMob->GetX(), pMob->GetY() );
ArcadiaServer::Instance().AddObject( pMob );
// enter 이펙트 끄자
pMob->SetFirstEnter( false );
}
// 월드에서 몬스터를 제거
void RemoveMonsterFromWorld( struct StructMonster * pMob )
{
if( !pMob->IsDead() ) pMob->quadTreeItem.RemoveMe();
pMob->onRemoveFromWorld();
ArcadiaServer::Instance().RemoveObject( pMob );
}
StructSummon * AllocNewSummon( struct StructPlayer* pMaster, struct StructItem* pCard )
{
StructSummon * pSummon = StructSummon::AllocSummon( pMaster, pCard->GetSummonCode() );
pSummon->SetSummonSID( StructPlayer::AllocSummonSID() );
pSummon->SetLevel( 1 );
pSummon->CalculateStat();
pSummon->SetParentCard( pCard );
if( pSummon->GetParentCard() )
pSummon->SetJP( pSummon->GetJobPoint() + GameContent::GetCreatureEnhanceInfo( pSummon->GetParentCard()->GetItemEnhance() )->jp_addition );
if( XRandom() % 100 < 1 )
{
pSummon->SetName( GameContent::GetUniqueName( pSummon->GetCreatureGroup() ).c_str() );
}
else
{
pSummon->SetName( GameContent::GetSummonName().c_str() );
}
pCard->SetSummonSID( pSummon->GetSummonSID() );
pCard->SetSummonStruct( pSummon );
return pSummon;
}
// 월드에 소환수를 추가
void AddSummonToWorld( struct StructSummon * pSummon )
{
pSummon->quadTreeItem.Set( *pSummon );
pSummon->quadTreeItem.AddMe();
pSummon->SetFirstEnter( true );
ArcadiaServer::Instance().AddObject( pSummon );
pSummon->SetSummonFlag( true );
// enter 이펙트 끄자
pSummon->SetFirstEnter( false );
ArcadiaServer::Instance().SetObjectPriority( pSummon, ArObject::UPDATE_PRIORITY_NORMAL );
}
// 월드에서 소환수를 제거
void RemoveSummonFromWorld( struct StructSummon * pSummon )
{
ArcadiaServer::Instance().SetObjectPriority( pSummon, ArObject::UPDATE_PRIORITY_IDLE );
pSummon->quadTreeItem.RemoveMe();
pSummon->onRemoveFromWorld();
ArcadiaServer::Instance().RemoveObject( pSummon );
}
struct StructPet * AllocNewPet( struct StructPlayer* pMaster, struct StructItem* pCage, const int nPetCode )
{
StructPet * pPet = StructPet::AllocPet( pMaster, nPetCode );
if( !pPet )
return NULL;
pPet->SetPetSID( StructPlayer::AllocPetSID() );
pPet->SetParentCage( pCage );
pPet->SetName( pPet->GetNameID() );
pPet->SetNameChanged( !pPet->IsRare() );
pPet->SetLoginComplete();
pCage->SetPetSID( pPet->GetPetSID() );
pCage->SetPetCode( nPetCode );
pCage->SetPetStruct( pPet );
pCage->SetInstanceFlagOn( ItemInstance::ITEM_FLAG_CONTAIN_PET );
return pPet;
}
// 월드에 펫을 추가
void AddPetToWorld( struct StructPet * pPet )
{
pPet->quadTreeItem.Set( *pPet );
pPet->quadTreeItem.AddMe();
pPet->SetFirstEnter( true );
ArcadiaServer::Instance().AddObject( pPet );
pPet->SetSummonFlag( true );
// enter 이펙트 끄자
pPet->SetFirstEnter( false );
ArcadiaServer::Instance().SetObjectPriority( pPet, ArObject::UPDATE_PRIORITY_NORMAL );
}
// 월드에서 펫을 제거
void RemovePetFromWorld( struct StructPet * pPet )
{
ArcadiaServer::Instance().SetObjectPriority( pPet, ArObject::UPDATE_PRIORITY_IDLE );
pPet->quadTreeItem.RemoveMe();
pPet->onRemoveFromWorld();
ArcadiaServer::Instance().RemoveObject( pPet );
}
// 플레이어 스탯 저장
void SavePlayer( struct StructPlayer *pPlayer )
{
pPlayer->Save();;
}
void SaveAllPlayer()
{
struct _PlayerSave : ArObjectFunctor
{
virtual void operator()( ArObject *pObj ) const
{
SavePlayer( static_cast< StructPlayer* >( pObj ) );
}
} _fo;
StructPlayer::DoEachPlayer( _fo );
}
void warpBeginSummon( struct StructPlayer *pPlayer, StructSummon * pSummon )
{
if( !pSummon || !pSummon->GetSummonFlag() || !pSummon->IsInWorld() )
return;
if( pSummon->IsUsingSkill() )
{
pSummon->CancelSkill();
}
RemoveSummonFromWorld( pSummon );
}
void WarpBegin( struct StructPlayer *pPlayer )
{
// TODO : 가끔 플레이어가 월드밖에 있을때가 있는듯.. (워프 두번 시도인가..?)
if( pPlayer->IsUsingSkill() )
{
pPlayer->CancelSkill();
}
if( pPlayer->IsTrading() )
{
StructPlayer::iterator itTradeTarget = StructPlayer::get( pPlayer->GetTradeTarget() );
StructPlayer * pTradeTarget = (*itTradeTarget);
if( pTradeTarget )
pTradeTarget->CancelTrade( true );
pPlayer->CancelTrade( true );
}
if( pPlayer->IsInWorld() )
{
RemovePlayerFromWorld( pPlayer );
}
StructSummon * pSummon = NULL;
if( pSummon = pPlayer->GetMainSummon() )
warpBeginSummon( pPlayer, pSummon );
if( pSummon = pPlayer->GetSubSummon() )
warpBeginSummon( pPlayer, pSummon );
StructPet *pPet = pPlayer->GetSummonedPet();
if( pPet && pPet->GetSummonFlag() && pPet->IsInWorld() )
{
if( pPet->IsUsingSkill() )
pPet->CancelSkill();
RemovePetFromWorld( pPlayer->GetSummonedPet() );
}
}
void warpEndSummon( struct StructPlayer *pPlayer, const struct ArPosition * pPosition, unsigned char layer, StructSummon * pSummon, bool bInDungeon )
{
if( !pSummon || !pSummon->GetSummonFlag() )
return;
pSummon->SetCurrentXY( pPosition->x, pPosition->y );
pSummon->SetCurrentLayer( layer );
pSummon->StopMove();
pSummon->SetFirstEnter( true );
if( pSummon != pPlayer->GetRideObject() )
{
ArPosition valid_pos;
if( GameContent::GetValidRandomPos( *pPosition, valid_pos, StructSummon::SUMMON_SPAWN_LENGTH ) == false )
{
valid_pos = pPlayer->GetPos();
}
pSummon->SetCurrentXY( valid_pos.x, valid_pos.y );
}
ArcadiaServer::Instance().AddObject( pSummon );
pSummon->SetFirstEnter( false );
ArPosition pos = pSummon->GetCurrentPosition( GetArTime() );
ArcadiaServer::Instance().SetMove( pSummon, pos, pos, 0, true, GetArTime() );
}
void WarpEnd( struct StructPlayer *pPlayer, const struct ArPosition * pPosition, unsigned char layer )
{
if( !pPlayer->GetSID() || !strlen( pPlayer->GetName() ) )
{
FileLogHandler::GetFileLogHandler()->LogStringEx( NULL, "BugReport", "Use No Character Login Bug [%s]", pPlayer->GetAccountName() );
GameRule::RegisterBlockAccount( pPlayer->GetAccountName() );
if( pPlayer->pConnection && pPlayer->pConnection->IsConnected() )
pPlayer->pConnection->Close();
else
{
StructPlayer::UnRegisterAccount( pPlayer->GetAccountName() );
}
return;
}
LOG::Log11N4S( LM_CHARACTER_WARP, pPlayer->GetAccountID(), pPlayer->GetSID(), 0, 0, pPlayer->GetLayer(), layer, pPlayer->GetX(), pPlayer->GetY(), pPosition->x, pPosition->y, 0, pPlayer->GetAccountName(), LOG::STR_NTS, pPlayer->GetName(), LOG::STR_NTS, "", 0, "", 0 );
int nChannelID = ChannelManager::GetChannelId( pPosition->GetX(), pPosition->GetY() );
if( layer != pPlayer->GetLayer() )
{
if( ChannelManager::GetChannelId( pPlayer->GetX(), pPlayer->GetY() ) && pPlayer->GetLayer() )
{
ChannelManager::LeavePlayerFromLayer( pPlayer->GetLayer() );
}
if( nChannelID && layer )
{
ChannelManager::EnterPlayerToLayer( layer );
}
}
pPlayer->SetCurrentXY( pPosition->x, pPosition->y );
pPlayer->SetCurrentLayer( layer );
pPlayer->StopMove();
pPlayer->SetWarpEnded( true );
SendWarpMessage( pPlayer );
StructSummon * pSummon = NULL;
if( pSummon = pPlayer->GetMainSummon() )
warpEndSummon( pPlayer, pPosition, layer, pSummon, DungeonManager::Instance().GetDungeonID( pPosition->x, pPosition->y ) != 0 );
if( pSummon = pPlayer->GetSubSummon() )
warpEndSummon( pPlayer, pPosition, layer, pSummon, DungeonManager::Instance().GetDungeonID( pPosition->x, pPosition->y ) != 0 );
StructPet *pPet = pPlayer->GetSummonedPet();
if( pPet )
{
ArPosition valid_pos;
if( GameContent::GetValidRandomPos( *pPosition, valid_pos, StructPet::PET_SPAWN_MIN_LENGTH ) == false )
{
valid_pos = pPlayer->GetPos();
}
pPet->SetCurrentXY( valid_pos.x, valid_pos.y );
pPet->SetCurrentLayer( layer );
pPet->StopMove();
pPet->SetFirstEnter( true );
ArcadiaServer::Instance().AddObject( pPet );
pPet->SetFirstEnter( false );
ArPosition pos = pPet->GetCurrentPosition( GetArTime() );
ArcadiaServer::Instance().SetMove( pPet, pos, pos, 0, true, GetArTime() );
}
pPlayer->SetInvincible( true );
if( pPlayer->GetMainSummon() )
{
pPlayer->GetMainSummon()->SetInvincible( true );
}
if( pPlayer->GetSubSummon() )
{
pPlayer->GetSubSummon()->SetInvincible( true );
}
pPlayer->SetFirstEnter( true );
ArcadiaServer::Instance().AddObject( pPlayer );
pPlayer->SetFirstEnter( false );
ArPosition pos = pPlayer->GetCurrentPosition( GetArTime() );
ArcadiaServer::Instance().SetMove( pPlayer, pos, pos, 0, true, GetArTime() );
SendPropertyMessage( pPlayer, pPlayer->GetHandle(), "channel", ChannelManager::GetChannelNum( layer ) );
pPlayer->ChangeLocation( pPlayer->GetX(), pPlayer->GetY() );
// 탑승 상태로 탑승 불가 지역으로 워프하는 경우 탑승 해제 처리
// * IsMountable() 함수가 캐릭터의 현재 WorldLocation 정보에 의존적인 처리여서 ChangeLocation 아래에서 처리함
// 뭣하면 ChangeLocation 함수 안쪽으로 넣어도 무방하지 않나 싶은데,
// 일단은 기존에 있던 코드에서 큰 변경을 만들지 않기 위해 처리되던 부분에서 위치만 변경함(ChannelManager 참조할 때는 ChangeLocation 위에서 처리했음)
if( ( pPlayer->IsRiding() || pPlayer->HasRidingState() ) && !pPlayer->IsMountable( false ) )
{
pPlayer->UnMount();
}
pPlayer->Save( true );
}
//Credits to AziaMafia: Direct inventory looting
void MonsterDropItemToInventory(struct StructPlayer* pClient, struct StructItem* pItem)
{
TS_CS_TAKE_ITEM info;
info.taker_handle = pClient->GetHandle();
info.item_handle = pItem->GetHandle();
ArcadiaServer::Instance().Broadcast(pItem->GetRX(), pItem->GetRY(), pItem->GetLayer(), &info);
onTakeItemFZ(pClient, static_cast<TS_CS_TAKE_ITEM*>(&info));
}
void onTakeItemFZ(struct StructPlayer* pClient, struct TS_CS_TAKE_ITEM* pMsg)
{
StructCreature* pTaker = pClient;
StructPet* pPet = pClient->GetSummonedPet();
StructItem* pItem = NULL;
StructItem::iterator it = StructItem::get(pMsg->item_handle);
if ((*it) && (*it)->IsItem())
{
pItem = static_cast<StructItem*>(*it);
}
if (pItem->GetWeight() > pClient->GetMaxWeight() - pClient->GetWeight())
{
SendResult(pClient, TM_CS_TAKE_ITEM , RESULT_TOO_HEAVY, pMsg->item_handle);
return;
}
const StructItem::ITEM_PICKUP_ORDER& ItemPickupOrder = pItem->GetPickupOrder();
for (int i = 0; i < 3; ++i)
{
if (!ItemPickupOrder.hPlayer[i] && ItemPickupOrder.nPartyID[i] == 0)
{
break;
}
if ((ItemPickupOrder.nPartyID[i] > 0 && ItemPickupOrder.nPartyID[i] == pClient->GetPartyID()) || ItemPickupOrder.hPlayer[i] == pClient->GetHandle())
{
break;
}
}
if (pItem->IsGold() && !pClient->IsInParty() && pClient->GetGold() + pItem->GetCount() > GameRule::MAX_GOLD_FOR_INVENTORY)
{
SendResult(pClient, TM_CS_TAKE_ITEM , RESULT_TOO_MUCH_MONEY, pMsg->item_handle);
return;
}
TS_SC_TAKE_ITEM_RESULT resultMsg;
resultMsg.item_handle = pMsg->item_handle;
resultMsg.item_taker = pTaker->GetHandle();
ArcadiaServer::Instance().Broadcast(pClient->GetRX(), pClient->GetRY(), pClient->GetLayer(), &resultMsg);
if (pClient->IsInParty())
{
if (pItem->IsGold())
{
std::vector< StructPlayer* > vList;
PartyManager::GetInstance().GetNearMember(pClient, 400, vList);
StructGold incGold(pItem->GetCount() / (float)vList.size());
const char* szType = "PICK";
if (vList.size() == 1) szType = "PPICK";
for (std::vector< StructPlayer* >::iterator it = vList.begin(); it != vList.end(); ++it)
{
StructGold nNewGold = (*it)->GetGold() + incGold;
//std::string new_item;
//std::string strResult;
//XStringUtil::Format(new_item, "return LootBot( '%s' , 1)", (*it)->GetName());
//LUA()->RunString(new_item.c_str(), &strResult);
//if (strResult == "true") continue;
if ((*it)->ChangeGold(nNewGold) != RESULT_SUCCESS)
{
PrintfChatMessage(false, CHAT_NOTICE, "@SYSTEM", (*it), "@578");
//LOG::Log11N4S(LM_ITEM_TAKE, (*it)->GetAccountID(), (*it)->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), 0, 0, 0, nNewGold.GetRawData(), pClient->GetX(), pClient->GetY(), 0, (*it)->GetAccountName(), LOG::STR_NTS, (*it)->GetName(), LOG::STR_NTS, "", 0, szType, LOG::STR_NTS);
continue;
}
//LOG::Log11N4S(LM_ITEM_TAKE, (*it)->GetAccountID(), (*it)->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), pItem->GetItemCode(), 0, 0, incGold.GetRawData(), nNewGold.GetRawData(), pClient->GetX(), pClient->GetY(), 0, (*it)->GetAccountName(), LOG::STR_NTS, (*it)->GetName(), LOG::STR_NTS, "", 0, szType, LOG::STR_NTS);
}
StructItem::PendFreeItem(pItem);
}
else
{
procPartyShareFZ(pClient, pItem);
}
}
else
{
AR_HANDLE hResultItem = procAddItemFZ(pClient, pItem, false);
if (!hResultItem)
{
assert(0);
StructItem::PendFreeItem(pItem);
//SendResult(pClient, TM_CS_TAKE_ITEM, RESULT_ACCESS_DENIED, pMsg->item_handle);
return;
}
SendResult(pClient, TM_CS_TAKE_ITEM, RESULT_SUCCESS, hResultItem);
}
}
void procPartyShareFZ(struct StructPlayer* pClient, struct StructItem* pItem)
{
if (!pItem->GetOwnerUID())
{
if (pItem->IsQuestItem() && pItem->IsQuestDistributeItem())
{
std::vector< StructPlayer* > vList;
PartyManager::GetInstance().GetNearMember(pClient, 400, vList);
std::vector< StructPlayer* >::iterator it;
for (it = vList.begin(); it != vList.end(); ++it)
{
if (!(*it)->IsTakeableQuestItem(pItem->GetItemCode()))
{
continue;
}
StructItem* pNewItem = StructItem::AllocItem(0, pItem->GetItemCode(), 1, ItemInstance::BY_MONSTER);
pNewItem->CopyFrom(pItem);
addPartyItemFZ((*it), pNewItem);
}
StructItem::PendFreeItem(pItem);
return;
}
PartyManager::_ITEM_SHARE_MODE eShareMode = PartyManager::GetInstance().GetShareMode(pClient->GetPartyID());
switch (eShareMode)
{
case PartyManager::ITEM_SHARE_MONOPOLY:
addPartyItemFZ(pClient, pItem);
break;
case PartyManager::ITEM_SHARE_LINEAR:
case PartyManager::ITEM_SHARE_RANDOM:
{
std::vector< StructPlayer* > vList;
PartyManager::GetInstance().GetNearMember(pClient, GameRule::PARTY_ITEM_SHARE_RANGE, vList);
std::vector< StructPlayer* >::iterator it;
for (it = vList.begin(); it != vList.end(); )
{
if ((*it)->GetMaxWeight() - (*it)->GetWeight() < pItem->GetCount() * pItem->GetItemBase().fWeight)
{
it = vList.erase(it);
continue;
}
if (pItem->IsQuestItem() && !(*it)->IsTakeableQuestItem(pItem->GetItemCode()))
{
it = vList.erase(it);
continue;
}
++it;
}
if (!vList.empty())
{
if (pItem->IsQuestItem())
{
addPartyItemFZ(PartyManager::GetInstance().GetNextItemAcquirer(pClient, vList), pItem);
}
else
{
if (eShareMode == PartyManager::ITEM_SHARE_RANDOM)
{
std::vector< StructPlayer* > vPlayerList(vList);
for (it = vList.begin(); it != vList.end(); )
{
if (!(*it)->HasItemPriority())
{
it = vList.erase(it);
continue;
}
++it;
}
if (vList.empty())
{
for (it = vPlayerList.begin(); it != vPlayerList.end(); ++it)
{
(*it)->SetItemPriority();
}
vList = vPlayerList;
}
}
StructPlayer* pAcquirer = PartyManager::GetInstance().GetNextItemAcquirer(pClient, vList);
addPartyItemFZ(pAcquirer, pItem);
pAcquirer->ReleaseItemPriority();
}
}
else
{
addPartyItemFZ(pClient, pItem);
}
}
break;
}
}
else
{
addPartyItemFZ(pClient, pItem);
}
}
bool addPartyItemFZ(struct StructPlayer* pClient, struct StructItem* pItem)
{
ItemBase::ItemCode nItemCode = pItem->GetItemCode();
int nItemLevel = pItem->GetItemLevel();
int nItemEnhance = pItem->GetItemEnhance();
__int64 nItemCount = pItem->GetCount();
AR_HANDLE hItemHandle = pItem->GetHandle();
AR_HANDLE hResultItem = procAddItemFZ(pClient, pItem);
if (!hResultItem)
{
//SendResult(pClient, TM_CS_TAKE_ITEM, RESULT_ACCESS_DENIED, hItemHandle);
return false;
}
SendResult(pClient, TM_CS_TAKE_ITEM, RESULT_SUCCESS, hResultItem);
PrintfPartyChatMessage(CHAT_PARTY_SYSTEM, pClient->GetPartyID(), "TAKE_ITEM|%s|%d|%d|%d|%I64d|", pClient->GetAlias(), nItemCode, nItemLevel, nItemEnhance, nItemCount);
return true;
}
const AR_HANDLE procAddItemFZ(struct StructPlayer* pClient, struct StructItem* pItem, bool bIsPartyProcess )
{
ItemBase::ItemCode code = pItem->GetItemCode();
//std::string new_item;
//std::string strResult;
//XStringUtil::Format(new_item, "return LootBot( '%s' , 2)" , pClient->GetName() );
//LUA()->RunString(new_item.c_str(), &strResult);
//if (strResult == "true") return 0;
if (!code && pClient->GetGold() + pItem->GetCount() > GameRule::MAX_GOLD_FOR_INVENTORY)
return 0;
// Anti Farm Bot Poids
if (pItem->GetWeight() > pClient->GetMaxWeight() - pClient->GetWeight()) return 0;
pItem->SetIdx(0);
StructItem* pNewItem = pClient->PushItem(pItem, pItem->GetCount());
// Event item pickup notice
if (pItem->GetEventDropFlag())
{
pItem->SetEventDropFlag(false);
int nFlag = 0;
pItem->GetInstanceFlag().CopyTo(&nFlag, sizeof(nFlag));
FileLogHandler::GetFileLogHandler()->LogStringEx(NULL, "Event", "%20s\t%20u\t%20d\t%20u", pClient->GetName(), pClient->GetSID(), code, nFlag);
if (GameRule::bBroadcastEventItemPickup)
{
PrintfGlobalChatMessage(CHAT_NOTICE, "@NOTICE", "@955\v#@picker_name@#\v%s\v#@item_name@#\v@%d", pClient->GetName(), pItem->GetItemBase().nNameId);
}
}
ItemUID uid = pNewItem ? pNewItem->GetItemUID() : 0;
AR_HANDLE item_handle = pNewItem ? pNewItem->GetHandle() : pItem->GetHandle();
const char* szType = "PICK";
if (bIsPartyProcess)
{
szType = "PPICK";
}
int x = pItem->GetX();
int y = pItem->GetY();
if (pItem->IsGold())
{
LOG::Log11N4S(LM_ITEM_TAKE, pClient->GetAccountID(), pClient->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), code, 0, 0, pItem->GetCount(), pClient->GetGold().GetRawData(), x, y, 0, pClient->GetAccountName(), LOG::STR_NTS, pClient->GetName(), LOG::STR_NTS, "", 0, szType, LOG::STR_NTS);
}
else
{
//LOG::Log11N4S(LM_ITEM_TAKE, pClient->GetAccountID(), pClient->GetSID(), pItem->GetItemEnhance() * 100 + pItem->GetItemLevel(), code, pItem->GetCount(), pNewItem ? pNewItem->GetCount() : 0, 0, 0, x, y, uid, pClient->GetAccountName(), LOG::STR_NTS, pClient->GetName(), LOG::STR_NTS, "", 0, szType, LOG::STR_NTS);
//std::string new_item;
//XStringUtil::Format(new_item, "procAddItem( '%s' , '%d' , '%d' )", pClient->GetName(), code, pItem->GetCount());
//LUA()->RunString(new_item.c_str());
}
if (pNewItem && pNewItem != pItem)
{
StructItem::PendFreeItem(pItem);
}
return item_handle;
}