From e8eb774458780019f59df34aa2ae8076c797d9d0 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 18 Oct 2014 00:17:46 -0400 Subject: [PATCH] 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. --- changelog.txt | 3 ++ common/database.cpp | 23 ++++++++--- common/database.h | 2 +- common/emu_oplist.h | 1 + common/eq_packet_structs.h | 6 +++ utils/patches/patch_RoF.conf | 1 + utils/patches/patch_SoD.conf | 1 + utils/patches/patch_SoF.conf | 1 + utils/patches/patch_Underfoot.conf | 1 + .../git/required/2014_10_18_group_mentor.sql | 2 + zone/client_packet.cpp | 32 +++++++++++++-- zone/client_packet.h | 1 + zone/exp.cpp | 17 ++++++-- zone/groups.cpp | 40 +++++++++++++++++++ zone/groups.h | 8 ++++ zone/worldserver.cpp | 5 ++- zone/zonedb.cpp | 2 +- 17 files changed, 131 insertions(+), 15 deletions(-) create mode 100644 utils/sql/git/required/2014_10_18_group_mentor.sql diff --git a/changelog.txt b/changelog.txt index c1b68b304..df7f664f4 100644 --- a/changelog.txt +++ b/changelog.txt @@ -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 diff --git a/common/database.cpp b/common/database.cpp index f08cf06b4..f36ab1713 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -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; } diff --git a/common/database.h b/common/database.h index 6fa9ef60a..71a6de9fb 100644 --- a/common/database.h +++ b/common/database.h @@ -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); diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 6ddd59123..c0ca0ca4e 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -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), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index a78b2afa1..d0792fd37 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -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; diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index e4eb9ba14..6bd486bfa 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -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 diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index 472d60e7a..0effd1b3c 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -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 diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index a50c1faf6..0b53daefd 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -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 diff --git a/utils/patches/patch_Underfoot.conf b/utils/patches/patch_Underfoot.conf index b7bdbc7e0..01cc1ad47 100644 --- a/utils/patches/patch_Underfoot.conf +++ b/utils/patches/patch_Underfoot.conf @@ -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 diff --git a/utils/sql/git/required/2014_10_18_group_mentor.sql b/utils/sql/git/required/2014_10_18_group_mentor.sql new file mode 100644 index 000000000..bd139f076 --- /dev/null +++ b/utils/sql/git/required/2014_10_18_group_mentor.sql @@ -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; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2d7df70ce..f759c9321 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -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)) { diff --git a/zone/client_packet.h b/zone/client_packet.h index 7cf7ce06b..0868b93ba 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -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); diff --git a/zone/exp.cpp b/zone/exp.cpp index a82aa25d9..c5c5cf69d 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -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(static_cast(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); diff --git a/zone/groups.cpp b/zone/groups.cpp index f0cdb71de..b9eda9836 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -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;iIsClient() && !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. diff --git a/zone/groups.h b/zone/groups.h index fe21ff3a4..250b86946 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -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 diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 1ff42cd02..be0a1a193 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -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); } } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 1ba36c5fd..e83edf2b9 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -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;