server/game/src/party.cpp

1710 lines
36 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 "stdafx.h"
#include "utils.h"
#include "char.h"
#include "party.h"
#include "char_manager.h"
#include "config.h"
#include "p2p.h"
#include "desc_client.h"
#include "dungeon.h"
#include "unique_item.h"
CPartyManager::CPartyManager()
{
Initialize();
}
CPartyManager::~CPartyManager()
{
}
void CPartyManager::Initialize()
{
m_bEnablePCParty = false;
}
void CPartyManager::DeleteAllParty()
{
TPCPartySet::iterator it = m_set_pkPCParty.begin();
while (it != m_set_pkPCParty.end())
{
DeleteParty(*it);
it = m_set_pkPCParty.begin();
}
}
bool CPartyManager::SetParty(LPCHARACTER ch) // PC¸¸ »ç¿ëÇØ¾ß ÇÑ´Ù!!
{
TPartyMap::iterator it = m_map_pkParty.find(ch->GetPlayerID());
if (it == m_map_pkParty.end())
return false;
LPPARTY pParty = it->second;
pParty->Link(ch);
return true;
}
void CPartyManager::P2PLogin(DWORD pid, const char* name)
{
TPartyMap::iterator it = m_map_pkParty.find(pid);
if (it == m_map_pkParty.end())
return;
it->second->UpdateOnlineState(pid, name);
}
void CPartyManager::P2PLogout(DWORD pid)
{
TPartyMap::iterator it = m_map_pkParty.find(pid);
if (it == m_map_pkParty.end())
return;
it->second->UpdateOfflineState(pid);
}
void CPartyManager::P2PJoinParty(DWORD leader, DWORD pid, BYTE role)
{
TPartyMap::iterator it = m_map_pkParty.find(leader);
if (it != m_map_pkParty.end())
{
it->second->P2PJoin(pid);
if (role >= PARTY_ROLE_MAX_NUM)
role = PARTY_ROLE_NORMAL;
it->second->SetRole(pid, role, true);
}
else
{
sys_err("No such party with leader [%d]", leader);
}
}
void CPartyManager::P2PQuitParty(DWORD pid)
{
TPartyMap::iterator it = m_map_pkParty.find(pid);
if (it != m_map_pkParty.end())
{
it->second->P2PQuit(pid);
}
else
{
sys_err("No such party with member [%d]", pid);
}
}
LPPARTY CPartyManager::P2PCreateParty(DWORD pid)
{
TPartyMap::iterator it = m_map_pkParty.find(pid);
if (it != m_map_pkParty.end())
return it->second;
LPPARTY pParty = M2_NEW CParty;
m_set_pkPCParty.insert(pParty);
SetPartyMember(pid, pParty);
pParty->SetPCParty(true);
pParty->P2PJoin(pid);
return pParty;
}
void CPartyManager::P2PDeleteParty(DWORD pid)
{
TPartyMap::iterator it = m_map_pkParty.find(pid);
if (it != m_map_pkParty.end())
{
m_set_pkPCParty.erase(it->second);
M2_DELETE(it->second);
}
else
sys_err("PARTY P2PDeleteParty Cannot find party [%u]", pid);
}
LPPARTY CPartyManager::CreateParty(LPCHARACTER pLeader)
{
if (pLeader->GetParty())
return pLeader->GetParty();
LPPARTY pParty = M2_NEW CParty;
if (pLeader->IsPC())
{
//TPacketGGParty p;
//p.header = HEADER_GG_PARTY;
//p.subheader = PARTY_SUBHEADER_GG_CREATE;
//p.pid = pLeader->GetPlayerID();
//P2P_MANAGER::instance().Send(&p, sizeof(p));
TPacketPartyCreate p;
p.dwLeaderPID = pLeader->GetPlayerID();
db_clientdesc->DBPacket(HEADER_GD_PARTY_CREATE, 0, &p, sizeof(TPacketPartyCreate));
sys_log(0, "PARTY: Create %s pid %u", pLeader->GetName(), pLeader->GetPlayerID());
pParty->SetPCParty(true);
pParty->Join(pLeader->GetPlayerID());
m_set_pkPCParty.insert(pParty);
}
else
{
pParty->SetPCParty(false);
pParty->Join(pLeader->GetVID());
}
pParty->Link(pLeader);
return (pParty);
}
void CPartyManager::DeleteParty(LPPARTY pParty)
{
//TPacketGGParty p;
//p.header = HEADER_GG_PARTY;
//p.subheader = PARTY_SUBHEADER_GG_DESTROY;
//p.pid = pParty->GetLeaderPID();
//P2P_MANAGER::instance().Send(&p, sizeof(p));
TPacketPartyDelete p;
p.dwLeaderPID = pParty->GetLeaderPID();
db_clientdesc->DBPacket(HEADER_GD_PARTY_DELETE, 0, &p, sizeof(TPacketPartyDelete));
m_set_pkPCParty.erase(pParty);
M2_DELETE(pParty);
}
void CPartyManager::SetPartyMember(DWORD dwPID, LPPARTY pParty)
{
TPartyMap::iterator it = m_map_pkParty.find(dwPID);
if (pParty == NULL)
{
if (it != m_map_pkParty.end())
m_map_pkParty.erase(it);
}
else
{
if (it != m_map_pkParty.end())
{
if (it->second != pParty)
{
it->second->Quit(dwPID);
it->second = pParty;
}
}
else
m_map_pkParty.insert(TPartyMap::value_type(dwPID, pParty));
}
}
EVENTINFO(party_update_event_info)
{
DWORD pid;
party_update_event_info()
: pid( 0 )
{
}
};
/////////////////////////////////////////////////////////////////////////////
//
// CParty begin!
//
/////////////////////////////////////////////////////////////////////////////
EVENTFUNC(party_update_event)
{
party_update_event_info* info = dynamic_cast<party_update_event_info*>( event->info );
if ( info == NULL )
{
sys_err( "party_update_event> <Factor> Null pointer" );
return 0;
}
DWORD pid = info->pid;
LPCHARACTER leader = CHARACTER_MANAGER::instance().FindByPID(pid);
if (leader && leader->GetDesc())
{
LPPARTY pParty = leader->GetParty();
if (pParty)
pParty->Update();
}
return PASSES_PER_SEC(3);
}
CParty::CParty()
{
Initialize();
}
CParty::~CParty()
{
Destroy();
}
void CParty::Initialize()
{
sys_log(2, "Party::Initialize");
m_iExpDistributionMode = PARTY_EXP_DISTRIBUTION_NON_PARITY;
m_pkChrExpCentralize = NULL;
m_dwLeaderPID = 0;
m_eventUpdate = NULL;
memset(&m_anRoleCount, 0, sizeof(m_anRoleCount));
memset(&m_anMaxRole, 0, sizeof(m_anMaxRole));
m_anMaxRole[PARTY_ROLE_LEADER] = 1;
m_anMaxRole[PARTY_ROLE_NORMAL] = 32;
m_dwPartyStartTime = get_dword_time();
m_iLongTimeExpBonus = 0;
m_dwPartyHealTime = get_dword_time();
m_bPartyHealReady = false;
m_bCanUsePartyHeal = false;
m_iLeadership = 0;
m_iExpBonus = 0;
m_iAttBonus = 0;
m_iDefBonus = 0;
m_itNextOwner = m_memberMap.begin();
m_iCountNearPartyMember = 0;
m_pkChrLeader = NULL;
m_bPCParty = false;
m_pkDungeon = NULL;
m_pkDungeon_for_Only_party = NULL;
}
void CParty::Destroy()
{
sys_log(2, "Party::Destroy");
// PC°¡ ¸¸µç ÆÄƼ¸é ÆÄƼ¸Å´ÏÀú¿¡ ¸Ê¿¡¼­ PID¸¦ »èÁ¦ÇØ¾ß ÇÑ´Ù.
if (m_bPCParty)
{
for (TMemberMap::iterator it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
CPartyManager::instance().SetPartyMember(it->first, NULL);
}
event_cancel(&m_eventUpdate);
RemoveBonus();
TMemberMap::iterator it = m_memberMap.begin();
DWORD dwTime = get_dword_time();
while (it != m_memberMap.end())
{
TMember & rMember = it->second;
++it;
if (rMember.pCharacter)
{
if (rMember.pCharacter->GetDesc())
{
TPacketGCPartyRemove p;
p.header = HEADER_GC_PARTY_REMOVE;
p.pid = rMember.pCharacter->GetPlayerID();
rMember.pCharacter->GetDesc()->Packet(&p, sizeof(p));
rMember.pCharacter->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ÆÄƼ> ÆÄƼ°¡ ÇØ»ê µÇ¾ú½À´Ï´Ù."));
}
else
{
// NPCÀÏ °æ¿ì ÀÏÁ¤ ½Ã°£ ÈÄ ÀüÅõ ÁßÀÌ ¾Æ´Ò ¶§ »ç¶óÁö°Ô ÇÏ´Â À̺¥Æ®¸¦ ½ÃÀÛ½ÃŲ´Ù.
rMember.pCharacter->SetLastAttacked(dwTime);
rMember.pCharacter->StartDestroyWhenIdleEvent();
}
rMember.pCharacter->SetParty(NULL);
}
}
m_memberMap.clear();
m_itNextOwner = m_memberMap.begin();
if (m_pkDungeon_for_Only_party != NULL)
{
m_pkDungeon_for_Only_party->SetPartyNull();
m_pkDungeon_for_Only_party = NULL;
}
}
void CParty::ChatPacketToAllMember(BYTE type, const char* format, ...)
{
char chatbuf[CHAT_MAX_LEN + 1];
va_list args;
va_start(args, format);
vsnprintf(chatbuf, sizeof(chatbuf), format, args);
va_end(args);
TMemberMap::iterator it;
for (it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
{
TMember & rMember = it->second;
if (rMember.pCharacter)
{
if (rMember.pCharacter->GetDesc())
{
rMember.pCharacter->ChatPacket(type, "%s", chatbuf);
}
}
}
}
DWORD CParty::GetLeaderPID()
{
return m_dwLeaderPID;
}
DWORD CParty::GetMemberCount()
{
return m_memberMap.size();
}
void CParty::P2PJoin(DWORD dwPID)
{
TMemberMap::iterator it = m_memberMap.find(dwPID);
if (it == m_memberMap.end())
{
TMember Member;
Member.pCharacter = NULL;
Member.bNear = false;
if (m_memberMap.empty())
{
Member.bRole = PARTY_ROLE_LEADER;
m_dwLeaderPID = dwPID;
}
else
Member.bRole = PARTY_ROLE_NORMAL;
if (m_bPCParty)
{
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(dwPID);
if (ch)
{
sys_log(0, "PARTY: Join %s pid %u leader %u", ch->GetName(), dwPID, m_dwLeaderPID);
Member.strName = ch->GetName();
if (Member.bRole == PARTY_ROLE_LEADER)
m_iLeadership = ch->GetLeadershipSkillLevel();
}
else
{
CCI * pcci = P2P_MANAGER::instance().FindByPID(dwPID);
if (!pcci);
else if (pcci->bChannel == g_bChannel)
Member.strName = pcci->szName;
else
sys_err("member is not in same channel PID: %u channel %d, this channel %d", dwPID, pcci->bChannel, g_bChannel);
}
}
sys_log(2, "PARTY[%d] MemberCountChange %d -> %d", GetLeaderPID(), GetMemberCount(), GetMemberCount()+1);
m_memberMap.insert(TMemberMap::value_type(dwPID, Member));
if (m_memberMap.size() == 1)
m_itNextOwner = m_memberMap.begin();
if (m_bPCParty)
{
CPartyManager::instance().SetPartyMember(dwPID, this);
SendPartyJoinOneToAll(dwPID);
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(dwPID);
if (ch)
SendParameter(ch);
}
}
if (m_pkDungeon)
{
m_pkDungeon->QuitParty(this);
}
}
void CParty::Join(DWORD dwPID)
{
P2PJoin(dwPID);
if (m_bPCParty)
{
TPacketPartyAdd p;
p.dwLeaderPID = GetLeaderPID();
p.dwPID = dwPID;
p.bState = PARTY_ROLE_NORMAL; // #0000790: [M2EU] CZ Å©·¡½¬ Áõ°¡: ÃʱâÈ­ Áß¿ä!
db_clientdesc->DBPacket(HEADER_GD_PARTY_ADD, 0, &p, sizeof(p));
}
}
void CParty::P2PQuit(DWORD dwPID)
{
TMemberMap::iterator it = m_memberMap.find(dwPID);
if (it == m_memberMap.end())
return;
if (m_bPCParty)
SendPartyRemoveOneToAll(dwPID);
if (it == m_itNextOwner)
IncreaseOwnership();
if (m_bPCParty)
RemoveBonusForOne(dwPID);
LPCHARACTER ch = it->second.pCharacter;
BYTE bRole = it->second.bRole;
m_memberMap.erase(it);
sys_log(2, "PARTY[%d] MemberCountChange %d -> %d", GetLeaderPID(), GetMemberCount(), GetMemberCount() - 1);
if (bRole < PARTY_ROLE_MAX_NUM)
{
--m_anRoleCount[bRole];
}
else
{
sys_err("ROLE_COUNT_QUIT_ERROR: INDEX(%d) > MAX(%d)", bRole, PARTY_ROLE_MAX_NUM);
}
if (ch)
{
ch->SetParty(NULL);
ComputeRolePoint(ch, bRole, false);
}
if (m_bPCParty)
CPartyManager::instance().SetPartyMember(dwPID, NULL);
// ¸®´õ°¡ ³ª°¡¸é ÆÄƼ´Â ÇØ»êµÇ¾î¾ß ÇÑ´Ù.
if (bRole == PARTY_ROLE_LEADER)
CPartyManager::instance().DeleteParty(this);
// ÀÌ ¾Æ·¡´Â Äڵ带 Ãß°¡ÇÏÁö ¸» °Í!!! À§ DeleteParty Çϸé this´Â ¾ø´Ù.
}
void CParty::Quit(DWORD dwPID)
{
// Always PC
P2PQuit(dwPID);
if (m_bPCParty && dwPID != GetLeaderPID())
{
//TPacketGGParty p;
//p.header = HEADER_GG_PARTY;
//p.subheader = PARTY_SUBHEADER_GG_QUIT;
//p.pid = dwPID;
//p.leaderpid = GetLeaderPID();
//P2P_MANAGER::instance().Send(&p, sizeof(p));
TPacketPartyRemove p;
p.dwPID = dwPID;
p.dwLeaderPID = GetLeaderPID();
db_clientdesc->DBPacket(HEADER_GD_PARTY_REMOVE, 0, &p, sizeof(p));
}
}
void CParty::Link(LPCHARACTER pkChr)
{
TMemberMap::iterator it;
if (pkChr->IsPC())
it = m_memberMap.find(pkChr->GetPlayerID());
else
it = m_memberMap.find(pkChr->GetVID());
if (it == m_memberMap.end())
{
sys_err("%s is not member of this party", pkChr->GetName());
return;
}
// Ç÷¹À̾î ÆÄƼÀÏ °æ¿ì ¾÷µ¥ÀÌÆ® À̺¥Æ® »ý¼º
if (m_bPCParty && !m_eventUpdate)
{
party_update_event_info* info = AllocEventInfo<party_update_event_info>();
info->pid = m_dwLeaderPID;
m_eventUpdate = event_create(party_update_event, info, PASSES_PER_SEC(3));
}
if (it->second.bRole == PARTY_ROLE_LEADER)
m_pkChrLeader = pkChr;
sys_log(2, "PARTY[%d] %s linked to party", GetLeaderPID(), pkChr->GetName());
it->second.pCharacter = pkChr;
pkChr->SetParty(this);
if (pkChr->IsPC())
{
if (it->second.strName.empty())
{
it->second.strName = pkChr->GetName();
}
SendPartyJoinOneToAll(pkChr->GetPlayerID());
SendPartyJoinAllToOne(pkChr);
SendPartyLinkOneToAll(pkChr);
SendPartyLinkAllToOne(pkChr);
SendPartyInfoAllToOne(pkChr);
SendPartyInfoOneToAll(pkChr);
SendParameter(pkChr);
//sys_log(0, "PARTY-DUNGEON connect %p %p", this, GetDungeon());
if (GetDungeon() && GetDungeon()->GetMapIndex() == pkChr->GetMapIndex())
{
pkChr->SetDungeon(GetDungeon());
}
RequestSetMemberLevel(pkChr->GetPlayerID(), pkChr->GetLevel());
}
}
void CParty::RequestSetMemberLevel(DWORD pid, BYTE level)
{
TPacketPartySetMemberLevel p;
p.dwLeaderPID = GetLeaderPID();
p.dwPID = pid;
p.bLevel = level;
db_clientdesc->DBPacket(HEADER_GD_PARTY_SET_MEMBER_LEVEL, 0, &p, sizeof(TPacketPartySetMemberLevel));
}
void CParty::P2PSetMemberLevel(DWORD pid, BYTE level)
{
if (!m_bPCParty)
return;
TMemberMap::iterator it;
sys_log(0, "PARTY P2PSetMemberLevel leader %d pid %d level %d", GetLeaderPID(), pid, level);
it = m_memberMap.find(pid);
if (it != m_memberMap.end())
{
it->second.bLevel = level;
}
}
namespace
{
struct FExitDungeon
{
void operator()(LPCHARACTER ch)
{
ch->ExitToSavedLocation();
}
};
}
void CParty::Unlink(LPCHARACTER pkChr)
{
TMemberMap::iterator it;
if (pkChr->IsPC())
it = m_memberMap.find(pkChr->GetPlayerID());
else
it = m_memberMap.find(pkChr->GetVID());
if (it == m_memberMap.end())
{
sys_err("%s is not member of this party", pkChr->GetName());
return;
}
if (pkChr->IsPC())
{
SendPartyUnlinkOneToAll(pkChr);
//SendPartyUnlinkAllToOne(pkChr); // ²÷±â´Â °ÍÀ̹ǷΠ±¸Áö Unlink ÆÐŶÀ» º¸³¾ ÇÊ¿ä ¾ø´Ù.
if (it->second.bRole == PARTY_ROLE_LEADER)
{
RemoveBonus();
if (it->second.pCharacter->GetDungeon())
{
// TODO: ´øÁ¯¿¡ ÀÖÀ¸¸é ³ª¸ÓÁöµµ ³ª°£´Ù
FExitDungeon f;
ForEachNearMember(f);
}
}
}
if (it->second.bRole == PARTY_ROLE_LEADER)
m_pkChrLeader = NULL;
it->second.pCharacter = NULL;
pkChr->SetParty(NULL);
}
void CParty::SendPartyRemoveOneToAll(DWORD pid)
{
TMemberMap::iterator it;
TPacketGCPartyRemove p;
p.header = HEADER_GC_PARTY_REMOVE;
p.pid = pid;
for (it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
{
if (it->second.pCharacter && it->second.pCharacter->GetDesc())
it->second.pCharacter->GetDesc()->Packet(&p, sizeof(p));
}
}
void CParty::SendPartyJoinOneToAll(DWORD pid)
{
const TMember& r = m_memberMap[pid];
TPacketGCPartyAdd p;
p.header = HEADER_GC_PARTY_ADD;
p.pid = pid;
strncpy(p.name, r.strName.c_str(), sizeof(p.name));
for (TMemberMap::iterator it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
{
if (it->second.pCharacter && it->second.pCharacter->GetDesc())
it->second.pCharacter->GetDesc()->Packet(&p, sizeof(p));
}
}
void CParty::SendPartyJoinAllToOne(LPCHARACTER ch)
{
if (!ch->GetDesc())
return;
TPacketGCPartyAdd p;
p.header = HEADER_GC_PARTY_ADD;
p.name[CHARACTER_NAME_MAX_LEN] = '\0';
for (TMemberMap::iterator it = m_memberMap.begin();it!= m_memberMap.end(); ++it)
{
p.pid = it->first;
strncpy(p.name, it->second.strName.c_str(), sizeof(p.name));
ch->GetDesc()->Packet(&p, sizeof(p));
}
}
void CParty::SendPartyUnlinkOneToAll(LPCHARACTER ch)
{
if (!ch->GetDesc())
return;
TMemberMap::iterator it;
TPacketGCPartyLink p;
p.header = HEADER_GC_PARTY_UNLINK;
p.pid = ch->GetPlayerID();
p.vid = (DWORD)ch->GetVID();
for (it = m_memberMap.begin();it!= m_memberMap.end(); ++it)
{
if (it->second.pCharacter && it->second.pCharacter->GetDesc())
{
it->second.pCharacter->GetDesc()->Packet(&p, sizeof(p));
}
}
}
void CParty::SendPartyLinkOneToAll(LPCHARACTER ch)
{
if (!ch->GetDesc())
return;
TMemberMap::iterator it;
TPacketGCPartyLink p;
p.header = HEADER_GC_PARTY_LINK;
p.vid = ch->GetVID();
p.pid = ch->GetPlayerID();
for (it = m_memberMap.begin();it!= m_memberMap.end(); ++it)
{
if (it->second.pCharacter && it->second.pCharacter->GetDesc())
{
it->second.pCharacter->GetDesc()->Packet(&p, sizeof(p));
}
}
}
void CParty::SendPartyLinkAllToOne(LPCHARACTER ch)
{
if (!ch->GetDesc())
return;
TMemberMap::iterator it;
TPacketGCPartyLink p;
p.header = HEADER_GC_PARTY_LINK;
for (it = m_memberMap.begin();it!= m_memberMap.end(); ++it)
{
if (it->second.pCharacter)
{
p.vid = it->second.pCharacter->GetVID();
p.pid = it->second.pCharacter->GetPlayerID();
ch->GetDesc()->Packet(&p, sizeof(p));
}
}
}
void CParty::SendPartyInfoOneToAll(DWORD pid)
{
TMemberMap::iterator it = m_memberMap.find(pid);
if (it == m_memberMap.end())
return;
if (it->second.pCharacter)
{
SendPartyInfoOneToAll(it->second.pCharacter);
return;
}
// Data Building
TPacketGCPartyUpdate p;
memset(&p, 0, sizeof(p));
p.header = HEADER_GC_PARTY_UPDATE;
p.pid = pid;
p.percent_hp = 255;
p.role = it->second.bRole;
for (it = m_memberMap.begin();it!= m_memberMap.end(); ++it)
{
if ((it->second.pCharacter) && (it->second.pCharacter->GetDesc()))
{
//sys_log(2, "PARTY send info %s[%d] to %s[%d]", ch->GetName(), (DWORD)ch->GetVID(), it->second.pCharacter->GetName(), (DWORD)it->second.pCharacter->GetVID());
it->second.pCharacter->GetDesc()->Packet(&p, sizeof(p));
}
}
}
void CParty::SendPartyInfoOneToAll(LPCHARACTER ch)
{
if (!ch->GetDesc())
return;
TMemberMap::iterator it;
// Data Building
TPacketGCPartyUpdate p;
ch->BuildUpdatePartyPacket(p);
for (it = m_memberMap.begin();it!= m_memberMap.end(); ++it)
{
if ((it->second.pCharacter) && (it->second.pCharacter->GetDesc()))
{
sys_log(2, "PARTY send info %s[%d] to %s[%d]", ch->GetName(), (DWORD)ch->GetVID(), it->second.pCharacter->GetName(), (DWORD)it->second.pCharacter->GetVID());
it->second.pCharacter->GetDesc()->Packet(&p, sizeof(p));
}
}
}
void CParty::SendPartyInfoAllToOne(LPCHARACTER ch)
{
TMemberMap::iterator it;
TPacketGCPartyUpdate p;
for (it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
{
if (!it->second.pCharacter)
{
DWORD pid = it->first;
memset(&p, 0, sizeof(p));
p.header = HEADER_GC_PARTY_UPDATE;
p.pid = pid;
p.percent_hp = 255;
p.role = it->second.bRole;
ch->GetDesc()->Packet(&p, sizeof(p));
continue;
}
it->second.pCharacter->BuildUpdatePartyPacket(p);
sys_log(2, "PARTY send info %s[%d] to %s[%d]", it->second.pCharacter->GetName(), (DWORD)it->second.pCharacter->GetVID(), ch->GetName(), (DWORD)ch->GetVID());
ch->GetDesc()->Packet(&p, sizeof(p));
}
}
void CParty::SendMessage(LPCHARACTER ch, BYTE bMsg, DWORD dwArg1, DWORD dwArg2)
{
if (ch->GetParty() != this)
{
sys_err("%s is not member of this party %p", ch->GetName(), this);
return;
}
switch (bMsg)
{
case PM_ATTACK:
break;
case PM_RETURN:
{
TMemberMap::iterator it = m_memberMap.begin();
while (it != m_memberMap.end())
{
TMember & rMember = it->second;
++it;
LPCHARACTER pkChr;
if ((pkChr = rMember.pCharacter) && ch != pkChr)
{
DWORD x = dwArg1 + Random::get(-500, 500);
DWORD y = dwArg2 + Random::get(-500, 500);
pkChr->SetVictim(NULL);
pkChr->SetRotationToXY(x, y);
if (pkChr->Goto(x, y))
{
LPCHARACTER victim = pkChr->GetVictim();
sys_log(0, "%s %p RETURN victim %p", pkChr->GetName(), get_pointer(pkChr), get_pointer(victim));
pkChr->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
}
}
}
}
break;
case PM_ATTACKED_BY: // °ø°Ý ¹Þ¾ÒÀ½, ¸®´õ¿¡°Ô µµ¿òÀ» ¿äû
{
// ¸®´õ°¡ ¾øÀ» ¶§
LPCHARACTER pkChrVictim = ch->GetVictim();
if (!pkChrVictim)
return;
TMemberMap::iterator it = m_memberMap.begin();
while (it != m_memberMap.end())
{
TMember & rMember = it->second;
++it;
LPCHARACTER pkChr;
if ((pkChr = rMember.pCharacter) && ch != pkChr)
{
if (pkChr->CanBeginFight())
pkChr->BeginFight(pkChrVictim);
}
}
}
break;
case PM_AGGRO_INCREASE:
{
LPCHARACTER victim = CHARACTER_MANAGER::instance().Find(dwArg2);
if (!victim)
return;
TMemberMap::iterator it = m_memberMap.begin();
while (it != m_memberMap.end())
{
TMember & rMember = it->second;
++it;
LPCHARACTER pkChr;
if ((pkChr = rMember.pCharacter) && ch != pkChr)
{
pkChr->UpdateAggrPoint(victim, DAMAGE_TYPE_SPECIAL, dwArg1);
}
}
}
break;
}
}
LPCHARACTER CParty::GetLeaderCharacter()
{
return m_memberMap[GetLeaderPID()].pCharacter;
}
bool CParty::SetRole(DWORD dwPID, BYTE bRole, bool bSet)
{
TMemberMap::iterator it = m_memberMap.find(dwPID);
if (it == m_memberMap.end())
{
return false;
}
LPCHARACTER ch = it->second.pCharacter;
if (bSet)
{
if (m_anRoleCount[bRole] >= m_anMaxRole[bRole])
return false;
if (it->second.bRole != PARTY_ROLE_NORMAL)
return false;
it->second.bRole = bRole;
if (ch && GetLeader())
ComputeRolePoint(ch, bRole, true);
if (bRole < PARTY_ROLE_MAX_NUM)
{
++m_anRoleCount[bRole];
}
else
{
sys_err("ROLE_COUNT_INC_ERROR: INDEX(%d) > MAX(%d)", bRole, PARTY_ROLE_MAX_NUM);
}
}
else
{
if (it->second.bRole == PARTY_ROLE_LEADER)
return false;
if (it->second.bRole == PARTY_ROLE_NORMAL)
return false;
it->second.bRole = PARTY_ROLE_NORMAL;
if (ch && GetLeader())
ComputeRolePoint(ch, PARTY_ROLE_NORMAL, false);
if (bRole < PARTY_ROLE_MAX_NUM)
{
--m_anRoleCount[bRole];
}
else
{
sys_err("ROLE_COUNT_DEC_ERROR: INDEX(%d) > MAX(%d)", bRole, PARTY_ROLE_MAX_NUM);
}
}
SendPartyInfoOneToAll(dwPID);
return true;
}
BYTE CParty::GetRole(DWORD pid)
{
TMemberMap::iterator it = m_memberMap.find(pid);
if (it == m_memberMap.end())
return PARTY_ROLE_NORMAL;
else
return it->second.bRole;
}
bool CParty::IsRole(DWORD pid, BYTE bRole)
{
TMemberMap::iterator it = m_memberMap.find(pid);
if (it == m_memberMap.end())
return false;
return it->second.bRole == bRole;
}
void CParty::RemoveBonus()
{
TMemberMap::iterator it;
for (it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
{
LPCHARACTER ch;
if ((ch = it->second.pCharacter))
{
ComputeRolePoint(ch, it->second.bRole, false);
}
it->second.bNear = false;
}
}
void CParty::RemoveBonusForOne(DWORD pid)
{
TMemberMap::iterator it = m_memberMap.find(pid);
if (it == m_memberMap.end())
return;
LPCHARACTER ch;
if ((ch = it->second.pCharacter))
ComputeRolePoint(ch, it->second.bRole, false);
}
void CParty::HealParty()
{
// XXX DELETEME Ŭ¶óÀ̾ðÆ® ¿Ï·áµÉ¶§±îÁö
{
return;
}
if (!m_bPartyHealReady)
return;
TMemberMap::iterator it;
LPCHARACTER l = GetLeaderCharacter();
for (it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
{
if (!it->second.pCharacter)
continue;
LPCHARACTER ch = it->second.pCharacter;
if (DISTANCE_APPROX(l->GetX()-ch->GetX(), l->GetY()-ch->GetY()) < PARTY_DEFAULT_RANGE)
{
ch->PointChange(POINT_HP, ch->GetMaxHP()-ch->GetHP());
ch->PointChange(POINT_SP, ch->GetMaxSP()-ch->GetSP());
}
}
m_bPartyHealReady = false;
m_dwPartyHealTime = get_dword_time();
}
void CParty::SummonToLeader(DWORD pid)
{
int xy[12][2] =
{
{ 250, 0 },
{ 216, 125 },
{ 125, 216 },
{ 0, 250 },
{ -125, 216 },
{ -216, 125 },
{ -250, 0 },
{ -216, -125 },
{ -125, -216 },
{ 0, -250 },
{ 125, -216 },
{ 216, -125 },
};
int n = 0;
int x[12], y[12];
SECTREE_MANAGER & s = SECTREE_MANAGER::instance();
LPCHARACTER l = GetLeaderCharacter();
if (m_memberMap.find(pid) == m_memberMap.end())
{
l->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ÆÄƼ> ¼ÒȯÇÏ·Á´Â ´ë»óÀ» ãÀ» ¼ö ¾ø½À´Ï´Ù."));
return;
}
LPCHARACTER ch = m_memberMap[pid].pCharacter;
if (!ch)
{
l->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ÆÄƼ> ¼ÒȯÇÏ·Á´Â ´ë»óÀ» ãÀ» ¼ö ¾ø½À´Ï´Ù."));
return;
}
if (!ch->CanSummon(m_iLeadership))
{
l->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ÆÄƼ> ´ë»óÀ» ¼ÒȯÇÒ ¼ö ¾ø½À´Ï´Ù."));
return;
}
for (int i = 0; i < 12; ++i)
{
PIXEL_POSITION p;
if (s.GetMovablePosition(l->GetMapIndex(), l->GetX() + xy [i][0], l->GetY() + xy[i][1], p))
{
x[n] = p.x;
y[n] = p.y;
n++;
}
}
if (n == 0)
l->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<ÆÄƼ> ÆÄƼ¿øÀ» ÇöÀç À§Ä¡·Î ¼ÒȯÇÒ ¼ö ¾ø½À´Ï´Ù."));
else
{
int i = Random::get(0, n - 1);
ch->Show(l->GetMapIndex(), x[i], y[i]);
ch->Stop();
}
}
void CParty::IncreaseOwnership()
{
if (m_memberMap.empty())
{
m_itNextOwner = m_memberMap.begin();
return;
}
if (m_itNextOwner == m_memberMap.end())
m_itNextOwner = m_memberMap.begin();
else
{
m_itNextOwner++;
if (m_itNextOwner == m_memberMap.end())
m_itNextOwner = m_memberMap.begin();
}
}
LPCHARACTER CParty::GetNextOwnership(LPCHARACTER ch, int x, int y)
{
if (m_itNextOwner == m_memberMap.end())
return ch;
int size = m_memberMap.size();
while (size-- > 0)
{
LPCHARACTER pkMember = m_itNextOwner->second.pCharacter;
if (pkMember && DISTANCE_APPROX(pkMember->GetX() - x, pkMember->GetY() - y) < 3000)
{
IncreaseOwnership();
return pkMember;
}
IncreaseOwnership();
}
return ch;
}
void CParty::ComputeRolePoint(LPCHARACTER ch, BYTE bRole, bool bAdd)
{
if (!bAdd)
{
ch->PointChange(POINT_PARTY_ATTACKER_BONUS, -ch->GetPoint(POINT_PARTY_ATTACKER_BONUS));
ch->PointChange(POINT_PARTY_TANKER_BONUS, -ch->GetPoint(POINT_PARTY_TANKER_BONUS));
ch->PointChange(POINT_PARTY_BUFFER_BONUS, -ch->GetPoint(POINT_PARTY_BUFFER_BONUS));
ch->PointChange(POINT_PARTY_SKILL_MASTER_BONUS, -ch->GetPoint(POINT_PARTY_SKILL_MASTER_BONUS));
ch->PointChange(POINT_PARTY_DEFENDER_BONUS, -ch->GetPoint(POINT_PARTY_DEFENDER_BONUS));
ch->PointChange(POINT_PARTY_HASTE_BONUS, -ch->GetPoint(POINT_PARTY_HASTE_BONUS));
ch->ComputeBattlePoints();
return;
}
//SKILL_POWER_BY_LEVEL
float k = (float) ch->GetSkillPowerByLevel(std::min<int>(SKILL_MAX_LEVEL, m_iLeadership)) / 100.0f;
//float k = (float) aiSkillPowerByLevel[std::min<int>(SKILL_MAX_LEVEL, m_iLeadership)] / 100.0f;
//
//sys_log(0,"ComputeRolePoint %fi %d, %d ", k, SKILL_MAX_LEVEL, m_iLeadership );
//END_SKILL_POWER_BY_LEVEL
switch (bRole)
{
case PARTY_ROLE_ATTACKER:
{
//int iBonus = (int) (10 + 90 * k);
int iBonus = (int) (10 + 60 * k);
if (ch->GetPoint(POINT_PARTY_ATTACKER_BONUS) != iBonus)
{
ch->PointChange(POINT_PARTY_ATTACKER_BONUS, iBonus - ch->GetPoint(POINT_PARTY_ATTACKER_BONUS));
ch->ComputePoints();
}
}
break;
case PARTY_ROLE_TANKER:
{
int iBonus = (int) (50 + 1450 * k);
if (ch->GetPoint(POINT_PARTY_TANKER_BONUS) != iBonus)
{
ch->PointChange(POINT_PARTY_TANKER_BONUS, iBonus - ch->GetPoint(POINT_PARTY_TANKER_BONUS));
ch->ComputePoints();
}
}
break;
case PARTY_ROLE_BUFFER:
{
int iBonus = (int) (5 + 45 * k);
if (ch->GetPoint(POINT_PARTY_BUFFER_BONUS) != iBonus)
{
ch->PointChange(POINT_PARTY_BUFFER_BONUS, iBonus - ch->GetPoint(POINT_PARTY_BUFFER_BONUS));
}
}
break;
case PARTY_ROLE_SKILL_MASTER:
{
int iBonus = (int) (25 + 600 * k);
if (ch->GetPoint(POINT_PARTY_SKILL_MASTER_BONUS) != iBonus)
{
ch->PointChange(POINT_PARTY_SKILL_MASTER_BONUS, iBonus - ch->GetPoint(POINT_PARTY_SKILL_MASTER_BONUS));
ch->ComputePoints();
}
}
break;
case PARTY_ROLE_HASTE:
{
int iBonus = (int) (1+5*k);
if (ch->GetPoint(POINT_PARTY_HASTE_BONUS) != iBonus)
{
ch->PointChange(POINT_PARTY_HASTE_BONUS, iBonus - ch->GetPoint(POINT_PARTY_HASTE_BONUS));
ch->ComputePoints();
}
}
break;
case PARTY_ROLE_DEFENDER:
{
int iBonus = (int) (5+30*k);
if (ch->GetPoint(POINT_PARTY_DEFENDER_BONUS) != iBonus)
{
ch->PointChange(POINT_PARTY_DEFENDER_BONUS, iBonus - ch->GetPoint(POINT_PARTY_DEFENDER_BONUS));
ch->ComputePoints();
}
}
break;
}
}
void CParty::Update()
{
sys_log(1, "PARTY::Update");
LPCHARACTER l = GetLeaderCharacter();
if (!l)
return;
TMemberMap::iterator it;
int iNearMember = 0;
bool bResendAll = false;
for (it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
{
LPCHARACTER ch = it->second.pCharacter;
it->second.bNear = false;
if (!ch)
continue;
if (l->GetDungeon())
it->second.bNear = l->GetDungeon() == ch->GetDungeon();
else
it->second.bNear = (DISTANCE_APPROX(l->GetX()-ch->GetX(), l->GetY()-ch->GetY()) < PARTY_DEFAULT_RANGE);
if (it->second.bNear)
{
++iNearMember;
//sys_log(0,"NEAR %s", ch->GetName());
}
}
if (iNearMember <= 1 && !l->GetDungeon())
{
for (it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
it->second.bNear = false;
iNearMember = 0;
}
if (iNearMember != m_iCountNearPartyMember)
{
m_iCountNearPartyMember = iNearMember;
bResendAll = true;
}
m_iLeadership = l->GetLeadershipSkillLevel();
int iNewExpBonus = ComputePartyBonusExpPercent();
m_iAttBonus = ComputePartyBonusAttackGrade();
m_iDefBonus = ComputePartyBonusDefenseGrade();
if (m_iExpBonus != iNewExpBonus)
{
bResendAll = true;
m_iExpBonus = iNewExpBonus;
}
bool bLongTimeExpBonusChanged = false;
// ÆÄƼ °á¼º ÈÄ ÃæºÐÇÑ ½Ã°£ÀÌ Áö³ª¸é °æÇèÄ¡ º¸³Ê½º¸¦ ¹Þ´Â´Ù.
if (!m_iLongTimeExpBonus && (get_dword_time() - m_dwPartyStartTime > PARTY_ENOUGH_MINUTE_FOR_EXP_BONUS * 60 * 1000 / (g_iUseLocale?1:2)))
{
bLongTimeExpBonusChanged = true;
m_iLongTimeExpBonus = 5;
bResendAll = true;
}
for (it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
{
LPCHARACTER ch = it->second.pCharacter;
if (!ch)
continue;
if (bLongTimeExpBonusChanged && ch->GetDesc())
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ÆÄƼÀÇ Çùµ¿·ÂÀÌ ³ô¾ÆÁ® Áö±ÝºÎÅÍ Ãß°¡ °æÇèÄ¡ º¸³Ê½º¸¦ ¹Þ½À´Ï´Ù."));
bool bNear = it->second.bNear;
ComputeRolePoint(ch, it->second.bRole, bNear);
if (bNear)
{
if (!bResendAll)
SendPartyInfoOneToAll(ch);
}
}
// PARTY_ROLE_LIMIT_LEVEL_BUG_FIX
m_anMaxRole[PARTY_ROLE_ATTACKER] = m_iLeadership >= 10 ? 1 : 0;
m_anMaxRole[PARTY_ROLE_HASTE] = m_iLeadership >= 20 ? 1 : 0;
m_anMaxRole[PARTY_ROLE_TANKER] = m_iLeadership >= 20 ? 1 : 0;
m_anMaxRole[PARTY_ROLE_BUFFER] = m_iLeadership >= 25 ? 1 : 0;
m_anMaxRole[PARTY_ROLE_SKILL_MASTER] = m_iLeadership >= 35 ? 1 : 0;
m_anMaxRole[PARTY_ROLE_DEFENDER] = m_iLeadership >= 40 ? 1 : 0;
m_anMaxRole[PARTY_ROLE_ATTACKER] += m_iLeadership >= 40 ? 1 : 0;
// END_OF_PARTY_ROLE_LIMIT_LEVEL_BUG_FIX
// Party Heal Update
if (!m_bPartyHealReady)
{
if (!m_bCanUsePartyHeal && m_iLeadership >= 18)
m_dwPartyHealTime = get_dword_time();
m_bCanUsePartyHeal = m_iLeadership >= 18; // Åë¼Ö·Â 18 ÀÌ»óÀº ÈúÀ» »ç¿ëÇÒ ¼ö ÀÖÀ½.
// Åë¼Ö·Â 40ÀÌ»óÀº ÆÄƼ Èú ÄðŸÀÓÀÌ Àû´Ù.
DWORD PartyHealCoolTime = (m_iLeadership >= 40) ? PARTY_HEAL_COOLTIME_SHORT * 60 * 1000 : PARTY_HEAL_COOLTIME_LONG * 60 * 1000;
if (m_bCanUsePartyHeal)
{
if (get_dword_time() > m_dwPartyHealTime + PartyHealCoolTime)
{
m_bPartyHealReady = true;
// send heal ready
if (0) // XXX DELETEME Ŭ¶óÀ̾ðÆ® ¿Ï·áµÉ¶§±îÁö
if (GetLeaderCharacter())
GetLeaderCharacter()->ChatPacket(CHAT_TYPE_COMMAND, "PartyHealReady");
}
}
}
if (bResendAll)
{
for (TMemberMap::iterator it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
if (it->second.pCharacter)
SendPartyInfoOneToAll(it->second.pCharacter);
}
}
void CParty::UpdateOnlineState(DWORD dwPID, const char* name)
{
TMember& r = m_memberMap[dwPID];
TPacketGCPartyAdd p;
p.header = HEADER_GC_PARTY_ADD;
p.pid = dwPID;
r.strName = name;
strncpy(p.name, name, sizeof(p.name));
for (TMemberMap::iterator it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
{
if (it->second.pCharacter && it->second.pCharacter->GetDesc())
it->second.pCharacter->GetDesc()->Packet(&p, sizeof(p));
}
}
void CParty::UpdateOfflineState(DWORD dwPID)
{
//const TMember& r = m_memberMap[dwPID];
TPacketGCPartyAdd p;
p.header = HEADER_GC_PARTY_ADD;
p.pid = dwPID;
memset(p.name, 0, CHARACTER_NAME_MAX_LEN+1);
for (TMemberMap::iterator it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
{
if (it->second.pCharacter && it->second.pCharacter->GetDesc())
it->second.pCharacter->GetDesc()->Packet(&p, sizeof(p));
}
}
int CParty::GetFlag(const std::string& name)
{
TFlagMap::iterator it = m_map_iFlag.find(name);
if (it != m_map_iFlag.end())
{
//sys_log(0,"PARTY GetFlag %s %d", name.c_str(), it->second);
return it->second;
}
//sys_log(0,"PARTY GetFlag %s 0", name.c_str());
return 0;
}
void CParty::SetFlag(const std::string& name, int value)
{
TFlagMap::iterator it = m_map_iFlag.find(name);
//sys_log(0,"PARTY SetFlag %s %d", name.c_str(), value);
if (it == m_map_iFlag.end())
{
m_map_iFlag.insert(make_pair(name, value));
}
else if (it->second != value)
{
it->second = value;
}
}
void CParty::SetDungeon(LPDUNGEON pDungeon)
{
m_pkDungeon = pDungeon;
m_map_iFlag.clear();
}
LPDUNGEON CParty::GetDungeon()
{
return m_pkDungeon;
}
void CParty::SetDungeon_for_Only_party(LPDUNGEON pDungeon)
{
m_pkDungeon_for_Only_party = pDungeon;
}
LPDUNGEON CParty::GetDungeon_for_Only_party()
{
return m_pkDungeon_for_Only_party;
}
bool CParty::IsPositionNearLeader(LPCHARACTER ch)
{
if (!m_pkChrLeader)
return false;
if (DISTANCE_APPROX(ch->GetX() - m_pkChrLeader->GetX(), ch->GetY() - m_pkChrLeader->GetY()) >= PARTY_DEFAULT_RANGE)
return false;
return true;
}
int CParty::GetExpBonusPercent()
{
if (GetNearMemberCount() <= 1)
return 0;
return m_iExpBonus + m_iLongTimeExpBonus;
}
bool CParty::IsNearLeader(DWORD pid)
{
TMemberMap::iterator it = m_memberMap.find(pid);
if (it == m_memberMap.end())
return false;
return it->second.bNear;
}
BYTE CParty::CountMemberByVnum(DWORD dwVnum)
{
if (m_bPCParty)
return 0;
LPCHARACTER tch;
BYTE bCount = 0;
TMemberMap::iterator it;
for (it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
{
if (!(tch = it->second.pCharacter))
continue;
if (tch->IsPC())
continue;
if (tch->GetMobTable().dwVnum == dwVnum)
++bCount;
}
return bCount;
}
void CParty::SendParameter(LPCHARACTER ch)
{
TPacketGCPartyParameter p;
p.bHeader = HEADER_GC_PARTY_PARAMETER;
p.bDistributeMode = m_iExpDistributionMode;
LPDESC d = ch->GetDesc();
if (d)
{
d->Packet(&p, sizeof(TPacketGCPartyParameter));
}
}
void CParty::SendParameterToAll()
{
if (!m_bPCParty)
return;
TMemberMap::iterator it;
for (it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
if (it->second.pCharacter)
SendParameter(it->second.pCharacter);
}
void CParty::SetParameter(int iMode)
{
if (iMode >= PARTY_EXP_DISTRIBUTION_MAX_NUM)
{
sys_err("Invalid exp distribution mode %d", iMode);
return;
}
m_iExpDistributionMode = iMode;
SendParameterToAll();
}
int CParty::GetExpDistributionMode()
{
return m_iExpDistributionMode;
}
void CParty::SetExpCentralizeCharacter(DWORD dwPID)
{
TMemberMap::iterator it = m_memberMap.find(dwPID);
if (it == m_memberMap.end())
return;
m_pkChrExpCentralize = it->second.pCharacter;
}
LPCHARACTER CParty::GetExpCentralizeCharacter()
{
return m_pkChrExpCentralize;
}
BYTE CParty::GetMemberMaxLevel()
{
BYTE bMax = 0;
itertype(m_memberMap) it = m_memberMap.begin();
while (it!=m_memberMap.end())
{
if (!it->second.bLevel)
{
++it;
continue;
}
if (!bMax)
bMax = it->second.bLevel;
else if (it->second.bLevel)
bMax = std::max(bMax, it->second.bLevel);
++it;
}
return bMax;
}
BYTE CParty::GetMemberMinLevel()
{
BYTE bMin = PLAYER_MAX_LEVEL_CONST;
itertype(m_memberMap) it = m_memberMap.begin();
while (it!=m_memberMap.end())
{
if (!it->second.bLevel)
{
++it;
continue;
}
if (!bMin)
bMin = it->second.bLevel;
else if (it->second.bLevel)
bMin = std::min(bMin, it->second.bLevel);
++it;
}
return bMin;
}
int CParty::ComputePartyBonusExpPercent()
{
if (GetNearMemberCount() <= 1)
return 0;
LPCHARACTER leader = GetLeaderCharacter();
int iBonusPartyExpFromItem = 0;
// UPGRADE_PARTY_BONUS
int iMemberCount = std::min<int>(8, GetNearMemberCount());
if (leader && (leader->IsEquipUniqueItem(UNIQUE_ITEM_PARTY_BONUS_EXP) || leader->IsEquipUniqueItem(UNIQUE_ITEM_PARTY_BONUS_EXP_MALL)
|| leader->IsEquipUniqueItem(UNIQUE_ITEM_PARTY_BONUS_EXP_GIFT) || leader->IsEquipUniqueGroup(10010)))
{
// Áß±¹Ãø À°µµ Àû¿ëÀ» È®ÀÎÇؾßÇÑ´Ù.
if (g_iUseLocale)
{
iBonusPartyExpFromItem = 30;
}
else
{
iBonusPartyExpFromItem = KOR_aiUniqueItemPartyBonusExpPercentByMemberCount[iMemberCount];
}
}
if (g_iUseLocale)
return iBonusPartyExpFromItem + CHN_aiPartyBonusExpPercentByMemberCount[iMemberCount];
else
return iBonusPartyExpFromItem + KOR_aiPartyBonusExpPercentByMemberCount[iMemberCount];
// END_OF_UPGRADE_PARTY_BONUS
}