Implement group mentor, leadership exp sharing (SoF+ only)

Currently only works in normal groups
Some decisions: the EXP will be rounded in the favor of the group leader
No idea how live actually handles it.
This commit is contained in:
Michael Cook (mackal) 2014-10-18 00:17:46 -04:00
parent 223d06645d
commit e8eb774458
17 changed files with 131 additions and 15 deletions

View File

@ -1,5 +1,8 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 10/18/2014==
demonstar55: Implement group mentor, sharing leadership exp (SoF+ only)
== 10/16/2014 ==
Uleat: Fixed the auto-conversion view naming error and renamed the views in the script files. Added a fix sql for databases that auto-converted.
Fix SQL: ../sql/git/bots/deprecated/2014_10_16_Lower_Case_View_Fix.sql

View File

@ -2963,7 +2963,7 @@ void Database::SetGroupLeaderName(uint32 gid, const char* name) {
return;
}
query = StringFormat("INSERT INTO group_leaders(gid, leadername, marknpc, leadershipaa, maintank, assist, puller) VALUES(%u, '%s', '', '', '', '', '')",
query = StringFormat("INSERT INTO group_leaders(gid, leadername, marknpc, leadershipaa, maintank, assist, puller, mentoree, mentor_percent) VALUES(%u, '%s', '', '', '', '', '', '', '0')",
gid, EscapeString(name).c_str());
result = QueryDatabase(query);
@ -2972,8 +2972,9 @@ void Database::SetGroupLeaderName(uint32 gid, const char* name) {
}
}
char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank, char* assist, char* puller, char *marknpc, GroupLeadershipAA_Struct* GLAA){
std::string query = StringFormat("SELECT `leadername`, `maintank`, `assist`, `puller`, `marknpc`, `leadershipaa` FROM `group_leaders` WHERE `gid` = %lu",(unsigned long)gid);
char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank, char* assist, char* puller, char *marknpc, char *mentoree, int *mentor_percent, GroupLeadershipAA_Struct* GLAA)
{
std::string query = StringFormat("SELECT `leadername`, `maintank`, `assist`, `puller`, `marknpc`, `mentoree`, `mentor_percent`, `leadershipaa` FROM `group_leaders` WHERE `gid` = %lu",(unsigned long)gid);
auto results = QueryDatabase(query);
if (!results.Success() || results.RowCount() == 0) {
@ -2992,6 +2993,12 @@ char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* mainta
if(marknpc)
marknpc[0] = '\0';
if (mentoree)
mentoree[0] = '\0';
if (mentor_percent)
*mentor_percent = 0;
return leaderbuf;
}
@ -3012,8 +3019,14 @@ char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* mainta
if(marknpc)
strcpy(marknpc, row[4]);
if(GLAA && results.LengthOfColumn(5) == sizeof(GroupLeadershipAA_Struct))
memcpy(GLAA, row[5], sizeof(GroupLeadershipAA_Struct));
if (mentoree)
strcpy(mentoree, row[5]);
if (mentor_percent)
*mentor_percent = atoi(row[6]);
if(GLAA && results.LengthOfColumn(7) == sizeof(GroupLeadershipAA_Struct))
memcpy(GLAA, row[7], sizeof(GroupLeadershipAA_Struct));
return leaderbuf;
}

View File

@ -203,7 +203,7 @@ public:
void SetGroupLeaderName(uint32 gid, const char* name);
char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr,
GroupLeadershipAA_Struct* GLAA = nullptr);
char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr);
void ClearGroupLeader(uint32 gid = 0);

View File

@ -207,6 +207,7 @@ N(OP_GroupInvite2),
N(OP_GroupLeaderChange),
N(OP_GroupLeadershipAAUpdate),
N(OP_GroupMakeLeader),
N(OP_GroupMentor),
N(OP_GroupRoles),
N(OP_GroupUpdate),
N(OP_GroupUpdateB),

View File

@ -2231,6 +2231,12 @@ struct GroupLeaderChange_Struct
/*128*/ char Unknown128[20];
};
struct GroupMentor_Struct {
/*000*/ int percent;
/*004*/ char name[64];
/*068*/
};
struct FaceChange_Struct {
/*000*/ uint8 haircolor;
/*001*/ uint8 beardcolor;

View File

@ -500,6 +500,7 @@ OP_GroupRoles=0x047c
OP_GroupMakeLeader=0x4129
OP_DoGroupLeadershipAbility=0x17d7
OP_GroupLeadershipAAUpdate=0x6567
OP_GroupMentor=0x56DB
# LFG/LFP Opcodes
OP_LFGCommand=0x4463

View File

@ -486,6 +486,7 @@ OP_GroupDisbandOther=0x162d
OP_GroupLeaderChange=0x7545
OP_GroupRoles=0x6b67
OP_GroupMakeLeader=0x6087
OP_GroupMentor=0x1224
# LFG/LFP Opcodes
OP_LFGCommand=0x3288 # C
OP_LFGGetMatchesRequest=0x5613 # C

View File

@ -452,6 +452,7 @@ OP_GroupDelete=0x0000 #
OP_CancelInvite=0x596C #Trevius 03/02/09
OP_GroupFollow2=0x59D4 #Xinu 02/20/09
OP_GroupInvite2=0x07F6 #Xinu 02/20/09
OP_GroupMentor=0x9EF3
#LFG/LFP Opcodes
OP_LFGCommand=0x5D81 #Trevius 01/16/09

View File

@ -489,6 +489,7 @@ OP_GroupDisbandOther=0x49f6 # C
OP_GroupLeaderChange=0x0c33 # C
OP_GroupRoles=0x116d # C
OP_GroupMakeLeader=0x5851
OP_GroupMentor=0x292f
# LFG/LFP Opcodes
OP_LFGCommand=0x2c38 # C

View File

@ -0,0 +1,2 @@
ALTER TABLE `group_leaders` ADD `mentoree` VARCHAR(64) NOT NULL;
ALTER TABLE `group_leaders` ADD `mentor_percent` INT(4) DEFAULT 0 NOT NULL;

View File

@ -234,6 +234,7 @@ void MapOpcodes()
ConnectedOpcodes[OP_GroupInvite] = &Client::Handle_OP_GroupInvite;
ConnectedOpcodes[OP_GroupInvite2] = &Client::Handle_OP_GroupInvite2;
ConnectedOpcodes[OP_GroupMakeLeader] = &Client::Handle_OP_GroupMakeLeader;
ConnectedOpcodes[OP_GroupMentor] = &Client::Handle_OP_GroupMentor;
ConnectedOpcodes[OP_GroupRoles] = &Client::Handle_OP_GroupRoles;
ConnectedOpcodes[OP_GroupUpdate] = &Client::Handle_OP_GroupUpdate;
ConnectedOpcodes[OP_GuildBank] = &Client::Handle_OP_GuildBank;
@ -1635,9 +1636,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
}
} //else, somebody from our group is already here...
if (group)
group->UpdatePlayer(this);
else
if (!group)
database.SetGroupID(GetName(), 0, CharacterID()); //cannot re-establish group, kill it
}
@ -1656,9 +1655,11 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
char AssistName[64];
char PullerName[64];
char NPCMarkerName[64];
char mentoree_name[64];
int mentor_percent;
GroupLeadershipAA_Struct GLAA;
memset(ln, 0, 64);
strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, &GLAA));
strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, mentoree_name, &mentor_percent, &GLAA));
Client *c = entity_list.GetClientByName(ln);
if (c)
group->SetLeader(c);
@ -1668,6 +1669,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
group->SetPuller(PullerName);
group->SetNPCMarker(NPCMarkerName);
group->SetGroupAAs(&GLAA);
group->SetGroupMentor(mentor_percent, mentoree_name);
//group->NotifyMainTank(this, 1);
//group->NotifyMainAssist(this, 1);
@ -1679,6 +1681,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
group->SendLeadershipAAUpdate();
}
group->UpdatePlayer(this);
LFG = false;
}
@ -6858,6 +6861,27 @@ void Client::Handle_OP_GroupMakeLeader(const EQApplicationPacket *app)
}
}
void Client::Handle_OP_GroupMentor(const EQApplicationPacket *app)
{
if (app->size != sizeof(GroupMentor_Struct)) {
LogFile->write(EQEMuLog::Error, "Wrong size: OP_GroupMentor, size=%i, expected %i", app->size, sizeof(GroupMentor_Struct));
DumpPacket(app);
return;
}
GroupMentor_Struct *gms = (GroupMentor_Struct *)app->pBuffer;
Group *group = GetGroup();
if (!group)
return;
gms->name[63] = '\0';
if (strlen(gms->name))
group->SetGroupMentor(gms->percent, gms->name);
else
group->ClearGroupMentor();
return;
}
void Client::Handle_OP_GroupRoles(const EQApplicationPacket *app)
{
if (app->size != sizeof(GroupRole_Struct)) {

View File

@ -138,6 +138,7 @@
void Handle_OP_GroupInvite(const EQApplicationPacket *app);
void Handle_OP_GroupInvite2(const EQApplicationPacket *app);
void Handle_OP_GroupMakeLeader(const EQApplicationPacket *app);
void Handle_OP_GroupMentor(const EQApplicationPacket *app);
void Handle_OP_GroupRoles(const EQApplicationPacket *app);
void Handle_OP_GroupUpdate(const EQApplicationPacket *app);
void Handle_OP_GuildBank(const EQApplicationPacket *app);

View File

@ -133,7 +133,7 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
}
}
if(IsLeadershipEXPOn() && ((conlevel == CON_BLUE) || (conlevel == CON_WHITE) || (conlevel == CON_YELLOW) || (conlevel == CON_RED))) {
if(IsLeadershipEXPOn() && (conlevel == CON_BLUE || conlevel == CON_WHITE || conlevel == CON_YELLOW || conlevel == CON_RED)) {
add_exp = static_cast<uint32>(static_cast<float>(add_exp) * 0.8f);
if(GetGroup())
@ -141,8 +141,19 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
if((m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel()))
&& (RuleI(Character, KillsPerGroupLeadershipAA) > 0))
{
AddLeadershipEXP(GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA), 0);
Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA);
Client *mentoree = GetGroup()->GetMentoree();
if (GetGroup()->GetMentorPercent() && mentoree &&
mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) {
uint32 mentor_exp = exp * (GetGroup()->GetMentorPercent() / 100.0f);
exp -= mentor_exp;
mentoree->AddLeadershipEXP(mentor_exp, 0); // ends up rounded down
mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
}
if (exp > 0) { // possible if you mentor 100% to the other client
AddLeadershipEXP(exp, 0); // ends up rounded up if mentored, no idea how live actually does it
Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
}
}
else
Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);

View File

@ -46,6 +46,7 @@ Group::Group(uint32 gid)
: GroupIDConsumer(gid)
{
leader = nullptr;
mentoree = nullptr;
memset(members,0,sizeof(Mob*) * MAX_GROUP_MEMBERS);
AssistTargetID = 0;
TankTargetID = 0;
@ -81,6 +82,7 @@ Group::Group(Mob* leader)
TankTargetID = 0;
PullerTargetID = 0;
memset(&LeaderAbilities, 0, sizeof(GroupLeadershipAA_Struct));
mentoree = nullptr;
uint32 i;
for(i=0;i<MAX_GROUP_MEMBERS;i++)
{
@ -467,6 +469,11 @@ bool Group::UpdatePlayer(Mob* update){
return true;
}
}
// mentoree isn't set, the name has a length and the name is ours! update the pointer
if (update->IsClient() && !mentoree && mentoree_name.length() && !mentoree_name.compare(update->GetName()))
mentoree = update->CastToClient();
return false;
}
@ -500,6 +507,9 @@ void Group::MemberZoned(Mob* removemob) {
if(removemob->IsClient() && HasRole(removemob, RolePuller))
SetGroupPullerTarget(0);
if (removemob->IsClient() && removemob == mentoree)
mentoree = nullptr;
}
bool Group::DelMemberOOZ(const char *Name) {
@ -528,6 +538,8 @@ bool Group::DelMemberOOZ(const char *Name) {
}
ClearAllNPCMarks();
}
if (Name == mentoree_name)
ClearGroupMentor();
return true;
}
}
@ -642,6 +654,9 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender)
UnDelegatePuller(oldmember->GetName());
}
if (oldmember->GetName() == mentoree_name)
ClearGroupMentor();
if(oldmember->IsClient())
SendMarkedNPCsToMember(oldmember->CastToClient(), true);
@ -1736,6 +1751,31 @@ void Group::SetGroupPullerTarget(Mob *m)
}
}
void Group::SetGroupMentor(int percent, char *name)
{
mentoree_name = name;
mentor_percent = percent;
Client *client = entity_list.GetClientByName(name);
mentoree = client ? client : nullptr;
std::string query = StringFormat("UPDATE group_leaders SET mentoree = '%s', mentor_percent = %i WHERE gid = %i LIMIT 1",
mentoree_name.c_str(), mentor_percent, GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to set group mentor: %s\n", results.ErrorMessage().c_str());
}
void Group::ClearGroupMentor()
{
mentoree_name.clear();
mentor_percent = 0;
mentoree = nullptr;
std::string query = StringFormat("UPDATE group_leaders SET mentoree = '', mentor_percent = 0 WHERE gid = %i LIMIT 1", GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to clear group mentor: %s\n", results.ErrorMessage().c_str());
}
void Group::NotifyAssistTarget(Client *c)
{
// Send a packet to the specified client notifying them of the group target selected by the Main Assist.

View File

@ -132,6 +132,11 @@ public:
const char *GetClientNameByIndex(uint8 index);
void UpdateXTargetMarkedNPC(uint32 Number, Mob *m);
void SetGroupMentor(int percent, char *name);
void ClearGroupMentor();
inline int GetMentorPercent() { return mentor_percent; }
inline Client *GetMentoree() { return mentoree; }
Mob* members[MAX_GROUP_MEMBERS];
char membername[MAX_GROUP_MEMBERS][64];
uint8 MemberRoles[MAX_GROUP_MEMBERS];
@ -151,6 +156,9 @@ private:
uint16 PullerTargetID;
uint16 MarkedNPCs[MAX_MARKED_NPCS];
std::string mentoree_name;
Client *mentoree;
int mentor_percent;
};
#endif

View File

@ -1001,9 +1001,11 @@ void WorldServer::Process() {
char AssistName[64];
char PullerName[64];
char NPCMarkerName[64];
char mentoree_name[64];
int mentor_percent;
GroupLeadershipAA_Struct GLAA;
memset(ln, 0, 64);
strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, &GLAA));
strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, mentoree_name, &mentor_percent, &GLAA));
Client *lc = entity_list.GetClientByName(ln);
if(lc)
group->SetLeader(lc);
@ -1013,6 +1015,7 @@ void WorldServer::Process() {
group->SetPuller(PullerName);
group->SetNPCMarker(NPCMarkerName);
group->SetGroupAAs(&GLAA);
group->SetGroupMentor(mentor_percent, mentoree_name);
}
}

View File

@ -2534,7 +2534,7 @@ void ZoneDatabase::RefreshGroupFromDB(Client *client){
gu->action = groupActUpdate;
strcpy(gu->yourname, client->GetName());
GetGroupLeadershipInfo(group->GetID(), gu->leadersname, nullptr, nullptr, nullptr, nullptr, &gu->leader_aas);
GetGroupLeadershipInfo(group->GetID(), gu->leadersname, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &gu->leader_aas);
gu->NPCMarkerID = group->GetNPCMarkerID();
int index = 0;