975 lines
29 KiB
C++
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;
|
|
} |