knightonline/server/GameServer/PartyHandler.cpp

651 lines
15 KiB
C++

#include "stdafx.h"
#include "../shared/KOSocketMgr.h"
#include <boost\foreach.hpp>
using namespace std;
void CUser::PartyProcess(Packet & pkt)
{
// TODO: Clean this entire system up.
string strUserID;
CUser *pUser;
uint8 opcode = pkt.read<uint8>();
switch (opcode)
{
case PARTY_CREATE: // Attempt to create a party.
case PARTY_INSERT: // Attempt to invite a user to an existing party.
pkt >> strUserID;
if (strUserID.empty() || strUserID.size() > MAX_ID_SIZE)
return;
pUser = g_pMain->GetUserPtr(strUserID, TYPE_CHARACTER);
if (pUser == nullptr)
return;
PartyRequest(pUser->GetSocketID(), (opcode == PARTY_CREATE));
break;
// Did the user we invited accept our party invite?
case PARTY_PERMIT:
if (pkt.read<uint8>())
PartyInsert();
else
PartyCancel();
break;
// Authorises a member as the new party leader
case PARTY_PROMOTE:
PartyPromote(pkt.read<uint16>());
break;
// Remove a user from our party.
case PARTY_REMOVE:
PartyRemove(pkt.read<uint16>());
break;
// Disband our party.
case PARTY_DELETE:
PartyDelete();
break;
}
}
void CUser::PartyCancel()
{
int leader_id = -1, count = 0;
if (!isInParty())
return;
_PARTY_GROUP *pParty = g_pMain->GetPartyPtr(GetPartyID());
if (pParty == nullptr)
return;
leader_id = pParty->uid[0];
CUser *pUser = g_pMain->GetUserPtr(leader_id);
if (pUser == nullptr)
return;
m_bInParty = false;
m_sPartyIndex = -1;
m_sPartyRequest = -1;
for (int i = 0; i < MAX_PARTY_USERS; i++)
{
if (pParty->uid[i] >= 0)
count++;
}
if (count == 1)
pUser->PartyDelete();
Packet result(WIZ_PARTY, uint8(PARTY_INSERT));
result << int16(-1);
pUser->Send(&result);
}
void CUser::PartyRequest(int memberid, bool bCreate)
{
Packet result;
int16 errorCode = -1, i=0;
_PARTY_GROUP* pParty = nullptr;
CUser *pUser = g_pMain->GetUserPtr(memberid);
if (pUser == nullptr
|| pUser == this
|| pUser->isInParty())
goto fail_return;
// Only allow partying of the enemy nation in Moradon or FT.
// Also, we threw in a check to prevent them from partying from other zones.
if ((GetNation() != pUser->GetNation() && (GetZoneID() != ZONE_MORADON && GetZoneID() != ZONE_MORADONM2)
&& GetZoneID() != ZONE_FORGOTTEN_TEMPLE && GetZoneID() != ZONE_LOST_TEMPLE)
|| GetZoneID() != pUser->GetZoneID()
|| (GetZoneID() == ZONE_DELOS && pUser->GetClanID() != GetClanID())
|| GetZoneID() == ZONE_CHAOS_DUNGEON
|| GetZoneID() == ZONE_STONE1 || GetZoneID() == ZONE_STONE2 || GetZoneID() == ZONE_STONE3)
{
errorCode = -3;
goto fail_return;
}
// Players taking the beginner/"chicken" quest can party anyone.
if (!m_bIsChicken)
{
// Players in the same clan can party together, no matter the level difference.
if (!isInClan()
|| GetClanID() != pUser->GetClanID())
{
if (!((pUser->GetLevel() <= (int)(GetLevel() * 1.5) && pUser->GetLevel() >= (int)(GetLevel() * 2 / 3))
|| (pUser->GetLevel() <= (GetLevel() + 8) && pUser->GetLevel() >= ((int)(GetLevel()) - 8))))
{
errorCode = -2;
goto fail_return;
}
}
}
if (!bCreate)
{
pParty = g_pMain->GetPartyPtr(GetPartyID());
if (pParty == nullptr)
goto fail_return;
for (i = 0; i < MAX_PARTY_USERS; i++)
{
if (pParty->uid[i] < 0)
break;
}
if (i == MAX_PARTY_USERS)
goto fail_return; // party is full
}
else
{
if (isInParty())
goto fail_return; // can't create a party if we're already in one
pParty = g_pMain->CreateParty(this);
if (pParty == nullptr)
goto fail_return;
m_bPartyLeader = true;
StateChangeServerDirect(6, 1); // give party leader the 'P' symbol
result.Initialize(AG_USER_PARTY);
result << uint8(PARTY_CREATE) << pParty->wIndex << pParty->uid[0];
Send_AIServer(&result);
}
pUser->m_sPartyRequest = m_sPartyIndex;
result.Initialize(WIZ_PARTY);
result << uint8(PARTY_PERMIT) << GetSocketID() << GetName();
pUser->Send(&result);
return;
fail_return:
result.Initialize(WIZ_PARTY);
result << uint8(PARTY_INSERT) << errorCode;
Send(&result);
}
void CUser::PartyInsert()
{
Packet result(WIZ_PARTY);
CUser* pUser = nullptr;
_PARTY_GROUP* pParty = nullptr;
uint8 byIndex = 0xFF;
int leader_id = -1;
m_sPartyIndex= m_sPartyRequest;
m_bInParty = true;
if (!isInParty())
return;
pParty = g_pMain->GetPartyPtr(GetPartyID());
if (pParty == nullptr)
{
m_bInParty = false;
m_sPartyIndex = -1;
return;
}
leader_id = pParty->uid[0];
pUser = g_pMain->GetUserPtr(leader_id);
if (pUser == nullptr)
return;
if (pUser->GetZoneID() != GetZoneID()
|| GetPartyMemberAmount(pParty) == MAX_PARTY_USERS)
{
PartyCancel();
return;
}
// make sure user isn't already in the array...
// kind of slow, but it works for the moment
foreach_array (i, pParty->uid)
{
if (pParty->uid[i] == GetSocketID())
{
m_bInParty = false;
m_sPartyIndex = -1;
pParty->uid[i] = -1;
return;
}
}
// Send the played who just joined the existing party list
for (int i = 0; i < MAX_PARTY_USERS; i++)
{
// No player set?
if (pParty->uid[i] < 0)
{
// If we're not in the list yet, add us.
if (byIndex == 0xFF)
{
pParty->uid[i] = GetSocketID();
byIndex = i;
}
continue;
}
// For everyone else,
pUser = g_pMain->GetUserPtr(pParty->uid[i]);
if (pUser == nullptr)
continue;
result.clear();
result << uint8(PARTY_INSERT) << pParty->uid[i]
<< uint8(1) // success
<< pUser->GetName()
<< pUser->m_iMaxHp << pUser->m_sHp
<< pUser->GetLevel() << pUser->m_sClass
<< pUser->m_iMaxMp << pUser->m_sMp
<< pUser->GetNation();
Send(&result);
}
pUser = g_pMain->GetUserPtr(pParty->uid[0]);
if (pUser == nullptr)
return;
if (pUser->m_bNeedParty == 2 && pUser->isInParty())
pUser->StateChangeServerDirect(2, 1);
if (m_bNeedParty == 2 && isInParty())
StateChangeServerDirect(2, 1);
result.clear();
result << uint8(PARTY_INSERT) << GetSocketID()
<< uint8(1) // success
<< GetName()
<< m_iMaxHp << m_sHp
<< GetLevel() << GetClass()
<< m_iMaxMp << m_sMp
<< GetNation();
g_pMain->Send_PartyMember(GetPartyID(), &result);
result.Initialize(AG_USER_PARTY);
result << uint8(PARTY_INSERT) << pParty->wIndex << byIndex << GetSocketID();
Send_AIServer(&result);
}
void CUser::PartyPromote(uint16 sMemberID)
{
// Only the existing party leader can promote a new party leader.
if (!isPartyLeader())
return;
// Ensure this user exists and that they're in our party already.
CUser * pUser = g_pMain->GetUserPtr(sMemberID);
if (pUser == nullptr
|| pUser->GetPartyID() != GetPartyID())
return;
// Ensure this party exists.
_PARTY_GROUP * pParty = g_pMain->GetPartyPtr(GetPartyID());
if (pParty == nullptr)
return;
// Find the position of the user to promote in the array.
uint8 pos = 0;
for (uint8 i = 1; i < MAX_PARTY_USERS; i++)
{
if (pParty->uid[i] != sMemberID)
continue;
pos = i;
break;
}
// Didn't find it? (leader's always position 0, no need to check there)
if (pos == 0)
return;
// Swap the IDs around, so they have the leadership position.
std::swap(pParty->uid[0], pParty->uid[pos]);
// Swap the seeking party & leader flags
std::swap(m_bNeedParty, pUser->m_bNeedParty);
std::swap(m_bPartyLeader, pUser->m_bPartyLeader);
// Remove our leadership state from the client
StateChangeServerDirect(6, 0); // remove 'P' symbol from old party leader
StateChangeServerDirect(2, m_bNeedParty); // seeking a party
// Make them leader.
pUser->StateChangeServerDirect(6, 1); // assign 'P' symbol to new party leader
pUser->StateChangeServerDirect(2, pUser->m_bNeedParty); // seeking a party
Packet result(WIZ_PARTY, uint8(PARTY_INSERT));
result << pUser->GetSocketID()
<< uint8(100) // reset position to leader
<< pUser->GetName()
<< pUser->m_iMaxHp << pUser->m_sHp
<< pUser->GetLevel() << pUser->GetClass()
<< pUser->m_iMaxMp << pUser->m_sMp
<< pUser->GetNation();
g_pMain->Send_PartyMember(GetPartyID(), &result);
// Now update the slots in the AI server.
result.Initialize(AG_USER_PARTY);
// Shift the ex-leader to the promoted player's slot
result << uint8(PARTY_INSERT) << pParty->wIndex << pos << GetSocketID();
Send_AIServer(&result);
// Shift the leader to the ex-leader's slot (0).
result << uint8(PARTY_INSERT) << pParty->wIndex << uint8(0) << pUser->GetSocketID();
Send_AIServer(&result);
}
void CUser::PartyRemove(int memberid)
{
if (!isInParty())
return;
CUser *pUser = g_pMain->GetUserPtr(memberid);
if (pUser == nullptr)
return;
_PARTY_GROUP *pParty = g_pMain->GetPartyPtr(GetPartyID());
if (pParty == nullptr)
{
m_bInParty = pUser->m_bInParty = false;
m_sPartyIndex = pUser->m_sPartyIndex = -1;
return;
}
if (memberid != GetSocketID())
{
if (pParty->uid[0] != GetSocketID())
return;
}
else
{
if (pParty->uid[0] == memberid)
{
PartyDelete();
return;
}
}
int count = 0, memberPos = -1;
for (int i = 0; i < MAX_PARTY_USERS; i++)
{
if (pParty->uid[i] < 0)
continue;
else if (pParty->uid[i] == memberid)
{
memberPos = i;
continue;
}
count++;
}
if (count == 1)
{
CUser *pPartyLeader = g_pMain->GetUserPtr(pParty->uid[0]);
if (pPartyLeader == nullptr)
return;
else
pPartyLeader->PartyDelete();
return;
}
Packet result(WIZ_PARTY, uint8(PARTY_REMOVE));
result << uint16(memberid);
g_pMain->Send_PartyMember(m_sPartyIndex, &result);
if (memberPos >= 0)
{
pUser->m_bInParty = false;
pUser->m_sPartyIndex = pParty->uid[memberPos] = -1;
}
// AI Server
result.Initialize(AG_USER_PARTY);
result << uint8(PARTY_REMOVE) << pParty->wIndex << uint16(memberid);
Send_AIServer(&result);
}
void CUser::PartyDelete()
{
if (!isInParty())
return;
_PARTY_GROUP *pParty = g_pMain->GetPartyPtr(GetPartyID());
if (pParty == nullptr)
{
m_bInParty = false;
m_sPartyIndex = -1;
return;
}
for (int i = 0; i < MAX_PARTY_USERS; i++)
{
CUser *pUser = g_pMain->GetUserPtr(pParty->uid[i]);
if (pUser != nullptr)
{
pUser->m_bInParty = false;
pUser->m_sPartyIndex = -1;
}
}
Packet result(WIZ_PARTY, uint8(PARTY_DELETE));
g_pMain->Send_PartyMember(pParty->wIndex, &result);
result.Initialize(AG_USER_PARTY);
m_bPartyLeader = false;
StateChangeServerDirect(6, 0); // remove 'P' symbol from party leader
result << uint8(PARTY_DELETE) << uint16(pParty->wIndex);
Send_AIServer(&result);
g_pMain->DeleteParty(pParty->wIndex);
}
// Seeking party system
void CUser::PartyBBS(Packet & pkt)
{
uint8 opcode = pkt.read<uint8>();
switch (opcode)
{
case PARTY_BBS_REGISTER:
PartyBBSRegister(pkt);
break;
case PARTY_BBS_DELETE:
PartyBBSDelete(pkt);
break;
case PARTY_BBS_NEEDED:
PartyBBSNeeded(pkt, PARTY_BBS_NEEDED);
break;
case PARTY_BBS_WANTED:
PartyBBSWanted(pkt);
break;
case PARTY_BBS_LIST:
SendPartyBBSNeeded(0, PARTY_BBS_LIST);
break;
}
}
void CUser::PartyBBSRegister(Packet & pkt)
{
int counter = 0;
if (isInParty() // You are already in a party!
|| m_bNeedParty == 2) // You are already on the BBS!
{
Packet result(WIZ_PARTY_BBS, uint8(PARTY_BBS_REGISTER));
result << uint8(0);
Send(&result);
return;
}
StateChangeServerDirect(2, 2); // seeking a party
// TODO: Make this a more localised map
SessionMap sessMap = g_pMain->m_socketMgr.GetActiveSessionMap();
BOOST_FOREACH (auto itr, sessMap)
{
CUser *pUser = TO_USER(itr.second);
if (pUser->GetNation() != GetNation()
|| pUser->m_bNeedParty == 1)
continue;
if(!((pUser->GetLevel() <= (int)(GetLevel() * 1.5) && pUser->GetLevel() >= (int)(GetLevel() * 1.5))
|| (pUser->GetLevel() <= (GetLevel() + 8) && pUser->GetLevel() >= ((int)(GetLevel()) - 8))))
continue;
if (pUser->GetSocketID() == GetSocketID()) break;
counter++;
}
SendPartyBBSNeeded(counter / MAX_BBS_PAGE, PARTY_BBS_LIST);
}
void CUser::PartyBBSDelete(Packet & pkt)
{
// You don't need anymore
if (m_bNeedParty == 1)
{
Packet result(WIZ_PARTY_BBS, uint8(PARTY_BBS_DELETE));
result << uint8(0);
Send(&result);
return;
}
StateChangeServerDirect(2, 1); // not looking for a party
SendPartyBBSNeeded(0, PARTY_BBS_DELETE);
}
void CUser::PartyBBSNeeded(Packet & pkt, uint8 type)
{
SendPartyBBSNeeded(pkt.read<uint16>(), type);
}
void CUser::SendPartyBBSNeeded(uint16 page_index, uint8 bType)
{
Packet result(WIZ_PARTY_BBS);
uint16 start_counter = 0, BBS_Counter = 0;
uint8 valid_counter = 0;
int j = 0;
start_counter = page_index * MAX_BBS_PAGE;
if (start_counter >= MAX_USER)
{
result << uint8(PARTY_BBS_NEEDED) << uint8(0);
Send(&result);
return;
}
//0B01 0000 0C 0002 00
result << bType << uint8(1) << page_index << uint8(0x0a) << uint8(0); //Not sure what the last 2 bytes are.
// TODO: Make this a more localised map
SessionMap sessMap = g_pMain->m_socketMgr.GetActiveSessionMap();
int i = -1; // start at -1, first iteration gets us to 0.+
#if __VERSION > 2005
result << uint16(0);
#endif
BOOST_FOREACH (auto itr, sessMap)
{
CUser *pUser = TO_USER(itr.second);
_PARTY_GROUP *pParty = nullptr;
string WantedMessage = "Seeking_Party";
uint8 PartyMembers = 0;
uint16 sClass = pUser->m_sClass;
i++;
if (GetZoneID() != pUser->GetZoneID()
|| (GetNation() != pUser->GetNation()
&& (GetZoneID() != ZONE_MORADON && GetZoneID() != ZONE_MORADONM2)
&& GetZoneID() != ZONE_FORGOTTEN_TEMPLE)
|| (pUser->m_bNeedParty == 1 && !pUser->m_bPartyLeader))
//|| !((pUser->GetLevel() <= (int)(GetLevel() * 1.5) && pUser->GetLevel() >= (int)(GetLevel() * 2 / 3))
//|| (pUser->GetLevel() <= (GetLevel() + 8) && pUser->GetLevel() >= ((int)(GetLevel()) - 8))))
continue;
BBS_Counter++;
if (i < start_counter
|| valid_counter >= MAX_BBS_PAGE)
continue;
if (pUser->m_bPartyLeader)
{
pParty = g_pMain->GetPartyPtr(pUser->GetPartyID());
if (pParty == nullptr) //Shouldn't be hit.
return;
WantedMessage = pParty->WantedMessage;
PartyMembers = GetPartyMemberAmount(pParty);
sClass = pParty->sWantedClass;
}
result.DByte();
result << pUser->GetName()
<< sClass
<< uint16(0) << pUser->GetLevel() //Not sure what that uint16 does.
<< uint8(0) << uint8(pUser->m_bPartyLeader ? 3 : 2); //2 is player, 3 is party leader
result.SByte();
result << WantedMessage
<< pUser->GetZoneID()
<< PartyMembers
<< pUser->GetNation();
#if __VERSION > 2005
result << uint8(0);
#endif
valid_counter++;
}
// You still need to fill up ten slots.
if (valid_counter < MAX_BBS_PAGE)
{
for (int j = valid_counter; j < MAX_BBS_PAGE; j++)
result << uint16(0) << uint16(0)
<< uint16(0) << uint8(0)
<< uint8(0) << uint8(0) << uint8(0)
<< uint16(0)
<< uint8(0);
}
result << page_index << BBS_Counter;
Send(&result);
}
void CUser::PartyBBSWanted(Packet & pkt)
{
uint16 page_index = 0;
if (!isPartyLeader())
return;
_PARTY_GROUP *pParty = g_pMain->GetPartyPtr(GetPartyID());
if (pParty == nullptr)
return;
pkt >> pParty->sWantedClass >> page_index >> pParty->WantedMessage;
SendPartyBBSNeeded(page_index, PARTY_BBS_WANTED);
}
uint8 CUser::GetPartyMemberAmount(_PARTY_GROUP *pParty)
{
if (pParty == nullptr)
pParty = g_pMain->GetPartyPtr(GetPartyID());
if (pParty == nullptr)
return 0;
uint8 PartyMembers = 0;
for (int i = 0; i < MAX_PARTY_USERS; i++)
{
if(pParty->uid[i] >= 0)
PartyMembers++;
}
return PartyMembers;
}