Fixed merc buffing bugs, added checks before dismissing merc to prevent possible bugged mercs, merged in Secret's merc memory leak fixes.

This commit is contained in:
badcaptain 2013-03-22 00:03:50 -04:00
parent 09df77b56e
commit a20c7735e8
6 changed files with 227 additions and 150 deletions

View File

@ -1,6 +1,11 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 03/21/2013 ==
Bad_Captain: Fixed merc buffing bugs.
Bad_Captain: Added checks before dismissing merc to prevent possible bugged mercs.
Bad_Captain: Merged in Secret's merc memory leak fixes.
== 03/18/2013 ==
Bad_Captain: Fixed zone crash due to merc focus effects & tribute.
Bad_Captain: Fixed merc aggro issues when client in melee range & spell recast timers.

View File

@ -1100,6 +1100,7 @@ public:
bool CheckCanHireMerc(Mob* merchant, uint32 template_id);
bool CheckCanRetainMerc(uint32 upkeep);
bool CheckCanUnsuspendMerc();
bool CheckCanDismissMerc();
inline uint32 GetMercID() const { return mercid; }
inline uint8 GetMercSlot() const { return mercSlot; }
void SetMercID( uint32 newmercid) { mercid = newmercid; }

View File

@ -6484,15 +6484,6 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app)
database.SetGroupID(inviter->GetName(), group->GetID(), inviter->CastToClient()->CharacterID());
database.SetGroupLeaderName(group->GetID(), inviter->GetName());
// Add the merc back into the new group
if (GetMerc())
{
if (GetMerc()->AddMercToGroup(GetMerc(), group))
{
database.SetGroupID(GetMerc()->GetName(), group->GetID(), inviter->CastToClient()->CharacterID(), true);
}
}
group->UpdateGroupAAs();
//Invite the inviter into the group first.....dont ask
@ -6527,11 +6518,6 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app)
if(!group->AddMember(this))
return;
if(GetMerc())
{
group->AddMember(GetMerc());
}
if(inviter->CastToClient()->IsLFP()) {
// If the player who invited us to a group is LFP, have them update world now that we have joined
// their group.
@ -6548,6 +6534,13 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app)
if(inviter->CastToClient()->GetClientVersion() >= EQClientSoD)
database.RefreshGroupFromDB(inviter->CastToClient());
// Add the merc back into the new group
if (GetMerc()) {
if (GetMerc()->AddMercToGroup(GetMerc(), group)) {
database.SetGroupID(GetMerc()->GetName(), group->GetID(), inviter->CastToClient()->CharacterID(), true);
}
}
//send updates to clients out of zone...
ServerPacket* pack = new ServerPacket(ServerOP_GroupJoin, sizeof(ServerGroupJoin_Struct));
ServerGroupJoin_Struct* gj = (ServerGroupJoin_Struct*)pack->pBuffer;
@ -6659,47 +6652,69 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app)
if(!memberToDisband)
memberToDisband = entity_list.GetMob(gd->name2);
if(memberToDisband )
{
if(group->IsLeader(this)) // the group leader can kick other members out of the group...
{
group->DelMember(memberToDisband,false);
if(memberToDisband ) {
if(group->IsLeader(this)) {
// the group leader can kick other members out of the group...
//group->DelMember(memberToDisband,false);
if(memberToDisband->IsClient())
{
group->DelMember(memberToDisband,false);
Client* memberClient = memberToDisband->CastToClient();
Merc* memberMerc = memberToDisband->CastToClient()->GetMerc();
if(memberMerc != NULL)
{
memberMerc->RemoveMercFromGroup(memberMerc, group);
if(!memberMerc->IsGrouped() && !memberClient->IsGrouped())
{
Group *g = new Group(memberClient);
if(memberMerc->AddMercToGroup(memberMerc, g)) {
entity_list.AddGroup(g);
database.SetGroupLeaderName(g->GetID(), memberClient->GetName());
g->SaveGroupLeaderAA();
database.SetGroupID(memberClient->GetName(), g->GetID(), memberClient->CharacterID());
database.SetGroupID(memberMerc->GetName(), g->GetID(), memberClient->CharacterID(), true);
database.RefreshGroupFromDB(memberClient);
}
}
memberMerc->RemoveMercFromGroup(memberMerc, group);
if(!memberMerc->IsGrouped() && !memberClient->IsGrouped()) {
Group *g = new Group(memberClient);
if(!g) {
delete g;
g = NULL;
return;
}
entity_list.AddGroup(g);
if(g->GetID() == 0) {
safe_delete(g);
return;
}
if(memberMerc->AddMercToGroup(memberMerc, g)) {
database.SetGroupLeaderName(g->GetID(), memberClient->GetName());
g->SaveGroupLeaderAA();
database.SetGroupID(memberClient->GetName(), g->GetID(), memberClient->CharacterID());
database.SetGroupID(memberMerc->GetName(), g->GetID(), memberClient->CharacterID(), true);
database.RefreshGroupFromDB(memberClient);
}
}
}
else if(memberToDisband->IsMerc())
{
else if(memberToDisband->IsMerc()) {
memberToDisband->CastToMerc()->RemoveMercFromGroup(memberToDisband->CastToMerc(), group);
memberToDisband->CastToMerc()->Suspend();
}
}
else
{ // ...but other members can only remove themselves
else {
// ...but other members can only remove themselves
group->DelMember(this,false);
if(!IsGrouped() && GetMerc() != NULL)
{
if(!IsGrouped() && GetMerc() != NULL) {
if(!IsGrouped()) {
Group *g = new Group(this);
Group *g = new Group(this);
if(!g) {
delete g;
g = NULL;
return;
}
entity_list.AddGroup(g);
if(g->GetID() == 0) {
safe_delete(g);
return;
}
if(GetMerc()->AddMercToGroup(GetMerc(), g)) {
entity_list.AddGroup(g);
database.SetGroupLeaderName(g->GetID(), this->GetName());
g->SaveGroupLeaderAA();
database.SetGroupID(this->GetName(), g->GetID(), this->CharacterID());
@ -13752,12 +13767,14 @@ void Client::Handle_OP_MercenaryDismiss(const EQApplicationPacket *app)
if(GetMercID()) {
Merc* merc = GetMerc();
if(merc)
merc->Dismiss();
if(merc) {
if(CheckCanDismissMerc()) {
merc->Dismiss();
}
}
}
SendMercMerchantResponsePacket(10);
//SendMercMerchantResponsePacket(10);
}
void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app)

View File

@ -214,6 +214,9 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte
if(!newmember && !NewMemberName)
return false;
if(GroupCount() >= MAX_GROUP_MEMBERS) //Sanity check for merging groups together.
return false;
if(!newmember)
InZone = false;
else

View File

@ -71,6 +71,7 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading)
}
Merc::~Merc() {
AI_Stop();
safe_delete(ourNPCData); //Since mercs are dynamically alloc'd we should probably safe_delete the data they were made from. I'm not entirely sure this is safe to delete a const.
entity_list.RemoveMerc(this->GetID());
UninitializeBuffSlots();
@ -1900,15 +1901,15 @@ void Merc::AI_Process() {
}
void Merc::AI_Start(int32 iMoveDelay) {
Mob::AI_Start(iMoveDelay);
NPC::AI_Start(iMoveDelay);
if (!pAIControlled)
return;
if (merc_spells.size() == 0) {
AIautocastspell_timer = new Timer(1000);
AIautocastspell_timer->SetTimer(1000);
AIautocastspell_timer->Disable();
} else {
AIautocastspell_timer = new Timer(750);
AIautocastspell_timer->SetTimer(750);
AIautocastspell_timer->Start(RandomTimer(0, 2000), false);
}
@ -2369,13 +2370,13 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) {
if(selectedMercSpell.spellid > 0) {
if(isDiscipline) {
castedSpell = UseDiscipline(selectedMercSpell.spellid, GetID());
castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetID());
}
else {
castedSpell = AIDoSpellCast(selectedMercSpell.spellid, this, -1, &TempDontBuffMeBeforeTime);
castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1, &TempDontBuffMeBeforeTime);
if(TempDontBuffMeBeforeTime != this->DontBuffMeBefore())
this->SetDontBuffMeBefore(TempDontBuffMeBeforeTime);
if(TempDontBuffMeBeforeTime != tar->DontBuffMeBefore())
tar->SetDontBuffMeBefore(TempDontBuffMeBeforeTime);
}
}
}
@ -2396,13 +2397,13 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) {
if(selectedMercSpell.spellid > 0) {
if(isDiscipline) {
castedSpell = UseDiscipline(selectedMercSpell.spellid, GetID());
castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetPet()->GetID());
}
else {
castedSpell = AIDoSpellCast(selectedMercSpell.spellid, this, -1, &TempDontBuffMeBeforeTime);
castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar->GetPet(), -1, &TempDontBuffMeBeforeTime);
if(TempDontBuffMeBeforeTime != this->DontBuffMeBefore())
this->SetDontBuffMeBefore(TempDontBuffMeBeforeTime);
if(TempDontBuffMeBeforeTime != tar->GetPet()->DontBuffMeBefore())
tar->GetPet()->SetDontBuffMeBefore(TempDontBuffMeBeforeTime);
}
}
}
@ -5167,8 +5168,8 @@ Merc* Merc::LoadMerc(Client *c, MercTemplate* merc_template, uint32 merchant_id,
snprintf(c->GetEPP().merc_name, 64, "%s", c->GetMercInfo().merc_name);
merc->SetSuspended(c->GetMercInfo().IsSuspended);
merc->gender = c->GetMercInfo().Gender;
merc->SetHP(c->GetMercInfo().hp);
merc->SetMana(c->GetMercInfo().mana);
merc->SetHP(c->GetMercInfo().hp <= 0 ? merc->GetMaxHP() : c->GetMercInfo().hp);
merc->SetMana(c->GetMercInfo().hp <= 0 ? merc->GetMaxMana() : c->GetMercInfo().mana);
merc->SetEndurance(c->GetMercInfo().endurance);
merc->luclinface = c->GetMercInfo().face;
merc->hairstyle = c->GetMercInfo().luclinHairStyle;
@ -5353,7 +5354,71 @@ void Client::UpdateMercTimer()
}
bool Client::CheckCanHireMerc(Mob* merchant, uint32 template_id) {
bool result = true;
MercTemplate* mercTemplate = zone->GetMercTemplate(template_id);
//invalid merc data
if(!mercTemplate) {
if (GetClientVersion() < EQClientRoF)
SendMercMerchantResponsePacket(9);
else
SendMercMerchantResponsePacket(10);
return false;
}
//check client version
if(GetClientVersion() < mercTemplate->ClientVersion) {
SendMercMerchantResponsePacket(3);
return false;
}
if(GetClientVersion() >= EQClientRoF && GetNumMercs() >= MAXMERCS) {
SendMercMerchantResponsePacket(6);
return false;
}
else if(GetMerc()) { //check for current merc
if (GetClientVersion() < EQClientRoF)
SendMercMerchantResponsePacket(6);
else
SendMercMerchantResponsePacket(6);
return false;
}
else if(GetMercInfo().mercid != 0 && GetMercInfo().IsSuspended) { //has suspended merc
if (GetClientVersion() < EQClientRoF)
SendMercMerchantResponsePacket(7);
else
SendMercMerchantResponsePacket(6);
return false;
}
//check for sufficient funds
if(RuleB(Mercs, ChargeMercPurchaseCost)) {
uint32 cost = Merc::CalcPurchaseCost(template_id, GetLevel()) * 100; // Cost is in gold
if(cost > 0 && !HasMoney(cost)) {
SendMercMerchantResponsePacket(1);
return false;
}
}
//check for raid
if(HasRaid()) {
SendMercMerchantResponsePacket(4);
return false;
}
//check group size
if(HasGroup() && GetGroup()->GroupCount() >= MAX_GROUP_MEMBERS) {
if (GetClientVersion() < EQClientRoF)
SendMercMerchantResponsePacket(8);
else
SendMercMerchantResponsePacket(7);
return false;
}
//check in combat
if(GetClientVersion() >= EQClientRoF && GetAggroCount() > 0) {
SendMercMerchantResponsePacket(8);
return false;
}
//check for valid merchant - can check near area for any merchants
if(!merchant) {
@ -5361,7 +5426,7 @@ bool Client::CheckCanHireMerc(Mob* merchant, uint32 template_id) {
SendMercMerchantResponsePacket(14);
else
SendMercMerchantResponsePacket(16);
result = false;
return false;
}
//check for merchant too far away
@ -5370,65 +5435,13 @@ bool Client::CheckCanHireMerc(Mob* merchant, uint32 template_id) {
SendMercMerchantResponsePacket(15);
else
SendMercMerchantResponsePacket(17);
result = false;
return false;
}
if(GetClientVersion() >= EQClientRoF && GetNumMercs() >= MAXMERCS) {
SendMercMerchantResponsePacket(6);
result = false;
}
else if(GetMerc()) { //check for current merc
if (GetClientVersion() < EQClientRoF)
SendMercMerchantResponsePacket(6);
else
SendMercMerchantResponsePacket(6);
result = false;
}
else if(GetMercInfo().mercid != 0 && GetMercInfo().IsSuspended) { //has suspended merc
if (GetClientVersion() < EQClientRoF)
SendMercMerchantResponsePacket(7);
else
SendMercMerchantResponsePacket(6);
result = false;
}
//check for sufficient funds
if(RuleB(Mercs, ChargeMercPurchaseCost)) {
uint32 cost = Merc::CalcPurchaseCost(template_id, GetLevel()) * 100; // Cost is in gold
if(cost > 0 && !HasMoney(cost)) {
SendMercMerchantResponsePacket(1);
result = false;
}
}
//check for raid
if(HasRaid()) {
SendMercMerchantResponsePacket(4);
result = false;
}
//check group size
if(HasGroup() && GetGroup()->GroupCount() == 6) {
if (GetClientVersion() < EQClientRoF)
SendMercMerchantResponsePacket(8);
else
SendMercMerchantResponsePacket(7);
result = false;
}
//check in combat
if(GetClientVersion() >= EQClientRoF && GetAggroCount() > 0) {
SendMercMerchantResponsePacket(8);
result = false;
}
return result;
return true;
}
bool Client::CheckCanRetainMerc(uint32 upkeep) {
bool result = true;
Merc* merc = GetMerc();
//check for sufficient funds
@ -5436,30 +5449,45 @@ bool Client::CheckCanRetainMerc(uint32 upkeep) {
if(merc) {
if(upkeep > 0 && !HasMoney(upkeep * 100)) {
SendMercMerchantResponsePacket(1);
result = false;
return false;
}
}
}
return result;
return true;
}
bool Client::CheckCanUnsuspendMerc() {
bool result = true;
MercTemplate* mercTemplate = zone->GetMercTemplate(GetMercInfo().MercTemplateID);
//invalid merc data
if(!mercTemplate) {
if (GetClientVersion() < EQClientRoF)
SendMercMerchantResponsePacket(9);
else
SendMercMerchantResponsePacket(10);
return false;
}
//check client version
if(GetClientVersion() < mercTemplate->ClientVersion) {
SendMercMerchantResponsePacket(3);
return false;
}
//check for raid
if(HasRaid()) {
SendMercMerchantResponsePacket(4);
result = false;
return false;
}
//check group size
if(HasGroup() && GetGroup()->GroupCount() == 6) {
if(HasGroup() && GetGroup()->GroupCount() >= MAX_GROUP_MEMBERS) {
if (GetClientVersion() < EQClientRoF)
SendMercMerchantResponsePacket(8);
else
SendMercMerchantResponsePacket(7);
result = false;
return false;
}
//check if zone allows mercs
@ -5468,15 +5496,30 @@ bool Client::CheckCanUnsuspendMerc() {
SendMercMerchantResponsePacket(4); // ??
else
SendMercMerchantResponsePacket(4); // ??
return false;
}
//check in combat
if(GetClientVersion() >= EQClientRoF && GetAggroCount() > 0) {
SendMercMerchantResponsePacket(8);
result = false;
return false;
}
return result;
return true;
}
bool Client::CheckCanDismissMerc() {
if(!GetMerc()) {
Message(7, "You have no mercenary to dismiss.");
return false;
}
if(GetMerc()->IsCasting()) {
Message(7, "Unable to dismiss mercenary.");
return false;
}
return true;
}
void Client::CheckMercSuspendTimer()
@ -5592,9 +5635,9 @@ bool Merc::Suspend() {
SetSuspended(true);
if(HasGroup()) {
/*if(HasGroup()) {
RemoveMercFromGroup(this, GetGroup());
}
}*/
Save();
@ -5643,40 +5686,45 @@ bool Merc::Unsuspend(bool setMaxStats) {
mercOwner->GetPTimers().Clear(&database, pTimerMercSuspend);
mercOwner->SendMercPersonalInfo();
Group* g = entity_list.GetGroupByClient(mercOwner);
if(!mercOwner->IsGrouped())
{
Group *g = new Group(mercOwner);
if(g && AddMercToGroup(this, g))
{
entity_list.AddGroup(g);
if(!g) { //nobody from our group is here... start a new group
g = new Group(mercOwner);
if(!g) {
delete g;
g = NULL;
return false;
}
entity_list.AddGroup(g);
if(g->GetID() == 0) {
delete g;
g = NULL;
return false;
}
if(AddMercToGroup(this, g)) {
entity_list.AddGroup(g, g->GetID());
database.SetGroupLeaderName(g->GetID(), mercOwner->GetName());
database.SetGroupID(mercOwner->GetName(), g->GetID(), mercOwner->CharacterID());
database.SetGroupID(this->GetName(), g->GetID(), mercOwner->CharacterID(), true);
database.RefreshGroupFromDB(mercOwner);
g->SaveGroupLeaderAA();
loaded = true;
}
else
{
if(MERC_DEBUG > 0)
mercOwner->Message(7, "Mercenary failed to join the group - Suspending");
Suspend();
safe_delete(g);
return false;
else {
g->DisbandGroup();
}
}
else if (AddMercToGroup(this, mercOwner->GetGroup()))
{
} //else, somebody from our group is already here...
else if (AddMercToGroup(this, mercOwner->GetGroup())) {
database.SetGroupID(GetName(), mercOwner->GetGroup()->GetID(), mercOwner->CharacterID(), true);
database.RefreshGroupFromDB(mercOwner);
loaded = true;
}
else
{
else {
if(MERC_DEBUG > 0)
mercOwner->Message(7, "Mercenary failed to join the group - Suspending");
@ -5903,10 +5951,12 @@ void Client::UpdateMercLevel() {
void Client::SendMercMerchantResponsePacket(int32 response_type) {
// This response packet brings up the Mercenary Manager window
EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryHire, sizeof(MercenaryMerchantResponse_Struct));
MercenaryMerchantResponse_Struct* mmr = (MercenaryMerchantResponse_Struct*)outapp->pBuffer;
mmr->ResponseType = response_type; // send specified response type
FastQueuePacket(&outapp);
if(GetClientVersion() >= EQClientSoD) {
EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryHire, sizeof(MercenaryMerchantResponse_Struct));
MercenaryMerchantResponse_Struct* mmr = (MercenaryMerchantResponse_Struct*)outapp->pBuffer;
mmr->ResponseType = response_type; // send specified response type
FastQueuePacket(&outapp);
}
}
void Client::SendMercenaryUnknownPacket(uint8 type) {

View File

@ -248,7 +248,8 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
npc_aggro = d->npc_aggro;
AI_Start();
if(!IsMerc())
AI_Start();
d_meele_texture1 = d->d_meele_texture1;
d_meele_texture2 = d->d_meele_texture2;