PDA

View Full Version : COMMITTED: Suspend Minion Patch


blmille2
01-14-2010, 02:28 AM
Hello, guys. I have the Suspend Minion AA working on my server (refreshed SVN yesterday).

Here's the diff:
Index: zone/AA.h
================================================== =================
--- zone/AA.h (revision 1094)
+++ zone/AA.h (working copy)
@@ -265,6 +265,7 @@
aaParagonofSpirit = 291, //DB
aaAdvancedInnateStrength = 292, //works
aaAdvancedInnateStamina = 302, //works
+ //aaSuspendMinion = 308, //WIP -- brandon
aaAdvancedInnateAgility = 312, //works
aaAdvancedInnateDexterity = 322, //works
aaAdvancedInnateIntelligence = 332, //works
Index: zone/client.cpp
================================================== =================
--- zone/client.cpp (revision 1094)
+++ zone/client.cpp (working copy)
@@ -4766,7 +4766,150 @@

return 0;
}
+int get_suspendedminion_spell_id(int account_id){
+ char errbuf[MYSQL_ERRMSG_SIZE];
+ char *query = 0;
+ int spell_id = 0;
+ MYSQL_RES *result;
+ MYSQL_ROW row;

+ if(account_id <=0)
+ return 0;
+
+ if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT spell_id from suspendedminions WHERE account_id=%0d",account_id), errbuf, &result)){
+ if (mysql_num_rows(result)>0){
+ row = mysql_fetch_row(result);
+ mysql_free_result(result);
+ if (strlen(row[0])>0){
+ spell_id=atoi(row[0]);
+ }
+ }
+ }
+ safe_delete_array(query);
+ return (spell_id > 0 ? spell_id : 0);
+}
+
+void delete_suspendedminions(int account_id){
+ char errbuf[MYSQL_ERRMSG_SIZE];
+ char *query = 0;
+
+ if(account_id <=0)
+ return;
+
+ database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM suspendedminions WHERE account_id=%0d",account_id), errbuf);//Deletes all suspended minions, just in case they happened to get more than one
+ safe_delete_array(query);
+}
+
+int get_suspendedminion_count(int account_id){
+ char errbuf[MYSQL_ERRMSG_SIZE];
+ char *query = 0;
+ int count = 0;
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+
+ if(account_id <=0)
+ return 0;
+
+ if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT COUNT(*) from suspendedminions WHERE account_id=%0d",account_id), errbuf, &result)){
+ if (mysql_num_rows(result)>0){
+ row = mysql_fetch_row(result);
+ mysql_free_result(result);
+ if (strlen(row[0])>0){
+ count=atoi(row[0]);
+ }
+ }
+ }
+ safe_delete_array(query);
+ return (count > 0 ? count : 0);
+}
+
+int get_spellid_from_npctypeid(int npc_type){
+ char errbuf[MYSQL_ERRMSG_SIZE];
+ char *query = 0;
+ int spell_id = 0;
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+
+ if(npc_type <= 0)
+ return 0;
+
+ if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT s.id FROM spells_new s INNER JOIN npc_types nt ON (s.teleport_zone=nt.name) WHERE nt.id=%0d LIMIT 1",npc_type), errbuf, &result)){
+ if (mysql_num_rows(result)>0){
+ row = mysql_fetch_row(result);
+ mysql_free_result(result);
+ if (strlen(row[0])>0){
+ spell_id=atoi(row[0]);
+ }
+ }
+ }
+ safe_delete_array(query);
+ return (spell_id > 0 ? spell_id : 0);
+}
+
+int add_suspendedminion(int account_id,int spell_id){
+ char errbuf[MYSQL_ERRMSG_SIZE];
+ char *query = 0;
+
+ if(account_id <=0 || spell_id <=0)
+ return -1;
+
+ database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO suspendedminions (account_id,spell_id) VALUES (%i,%i)", account_id, spell_id));
+ safe_delete_array(query);
+ return 0;
+}
+void Client::SuspendMinion(){
+ Client *c = this;
+ char errbuf[MYSQL_ERRMSG_SIZE];
+ char *query = 0;
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+
+ Mob *pet = c->GetPet();
+
+ int cur_level=c->GetAA(aaID::aaSuspendedMinion);//SuspendedMinion ID
+
+ if(cur_level==0){
+ c->Message(0," - You don't have that AA!");
+ return;
+ }
+
+ //TODO: Right now, get the pet to pop and depop--worry about ranks later
+ int spell_id=0;
+ int minion_count = get_suspendedminion_count(c->AccountID());
+ if(pet==0){
+ if(minion_count > 0){
+ spell_id = get_suspendedminion_spell_id(c->AccountID());
+ if(spell_id){
+ c->Message(0,"Your pet falls out of your pocket!");
+ c->MakePet(spell_id,spells[spell_id].teleport_zone);
+ delete_suspendedminions(c->AccountID());
+ }else{
+ c->Message(13,"Error: Couldn't find the spell id for your pet!");
+ return;
+ }
+ }else{
+ c->Message(0,"You check your pocket, but find no minion!");
+ return;
+ }
+ }else{
+ spell_id=get_spellid_from_npctypeid(pet->GetNPCTypeID());
+ if(spell_id){
+ if(minion_count){
+ c->Message(13,"You try to force your pet in your pocket, but your other pet snaps at you.");
+ return;
+ }else{
+ add_suspendedminion(c->AccountID(),spell_id);
+ pet->Kill();
+ c->Message(0,"You open your pocket and your pet hops in.");
+ }
+ }else{
+ c->Message(13,"Error getting the spell_id from the npc_type id.");
+ return;
+ }
+ }
+
+}
+
void Client::SummonAndRezzAllCorpses()
{
pendingrezzexp = -1;
Index: zone/client.h
================================================== =================
--- zone/client.h (revision 1094)
+++ zone/client.h (working copy)
@@ -894,6 +894,7 @@
void CalcItemScale();
bool CalcItemScale(int32 slot_x, int32 slot_y);
void SummonAndRezzAllCorpses();
+ void SuspendMinion();
void NotifyNewTitlesAvailable();
void Signal(int32 data);
Mob *GetBindSightTarget() { return bind_sight_target; }
Index: zone/command.cpp
================================================== =================
--- zone/command.cpp (revision 1094)
+++ zone/command.cpp (working copy)
@@ -341,6 +341,7 @@
command_add("guilds",NULL,0,command_guild) ||
command_add("zonestatus","- Show connected zoneservers, synonymous with /servers",150,command_zonestatus) ||
command_add("manaburn","- Use AA Wizard class skill manaburn on target",10,command_manaburn) ||
+ command_add("suspendminion","- Use pet class AA skill Suspend Minion",10,command_suspendminion) ||
command_add("viewmessage","[id] - View messages in your tell queue",100,command_viewmessage) ||
command_add("viewmessages",NULL,0,command_viewmessage) ||
command_add("doanim","[animnum] [type] - Send an EmoteAnim for you or your target",50,command_doanim) ||
@@ -5158,6 +5159,11 @@
}
}

+void command_suspendminion(Client *c, const Seperator *sep)
+{
+ c->SuspendMinion();
+}
+
void command_viewmessage(Client *c, const Seperator *sep)
{
char errbuf[MYSQL_ERRMSG_SIZE];
Index: zone/command.h
================================================== =================
--- zone/command.h (revision 1094)
+++ zone/command.h (working copy)
@@ -218,6 +218,7 @@
bool helper_guild_edit(Client *c, int32 dbid, int32 eqid, int8 rank, const char* what, const char* value);
void command_zonestatus(Client *c, const Seperator *sep);
void command_manaburn(Client *c, const Seperator *sep);
+void command_suspendminion(Client *c, const Seperator *sep);
void command_viewmessage(Client *c, const Seperator *sep);
void command_doanim(Client *c, const Seperator *sep);
void command_randomfeatures(Client *c, const Seperator *sep);
Index: zone/pets.cpp
================================================== =================
--- zone/pets.cpp (revision 1094)
+++ zone/pets.cpp (working copy)
@@ -213,8 +213,6 @@
return base_hp;
}
*/
-
-
void Mob::MakePet(int16 spell_id, const char* pettype, const char *petname) {
//see if we are a special type of pet (for command filtering)
PetType type = petOther;
Index: zone/spell_effects.cpp
================================================== =================
--- zone/spell_effects.cpp (revision 1094)
+++ zone/spell_effects.cpp (working copy)
@@ -2732,6 +2732,10 @@
CastToClient()->GoToBind(4);
break;
}
+ case SE_SuspendMinion:
+ {
+ CastToClient()->SuspendMinion();
+ }

case SE_ImprovedDamage:
case SE_ImprovedHeal:

blmille2
01-14-2010, 02:35 AM
mysql> explain suspendedminions;
+------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+-------+
| account_id | int(11) | NO | MUL | NULL | |
| spell_id | int(11) | NO | | NULL | |
+------------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql>

steve
01-14-2010, 03:22 AM
Those client messages aren't what is used on Live.

blmille2
01-14-2010, 03:25 AM
You're right. I completely made them up so that I had some idea as to what was going on.

Do you have a link to the client messages?

Thanks!

KLS
01-14-2010, 04:34 AM
First of all that's pretty hard to read use the code /code blocks to create a field that will preserve tabs.

Also given that the skill requires staying in the current zone I'm not sold that it needs a database entry or calls. It would in fact be both faster computationally and easier to implement if you created an object in the Client class to store the suspended pet I think.

Also not a fan of the c style functions but that's more stylistic than anything being wrong with them.

blmille2
01-14-2010, 04:54 AM
Good catch on the no-zone thing. I thought you were able to zone with it after they changed live to allow you to zone and keep your pet.

I'll rework this sometime in the near future and repost (with /code).

Thanks!

P.S. I'll do a search on the accepted coding style here in a bit.

steve
01-14-2010, 12:16 PM
You can zone with a suspended pet, but there's another AA that enables that.

Next time I'm playing Live, I'll try to note down the messages you get from using the AA.

blmille2
01-14-2010, 07:02 PM
Well, what do you guys think?

I can go ahead and implement suspend minion to use a database for when we can use the "zone with suspended minion" AA. If that's the case, then I just have to do some minor edits before that AA can work. Otherwise, I'll have to rewrite it.

However, it would be easier implement Suspend Minion to not use the database and just use a class to store the suspended minion information.

I'm more than happy to go either way on this issue.

If I don't hear anything, I'll go ahead and start over to use the class idea so that suspended minions are forgotten after zone.

joligario
01-14-2010, 08:40 PM
Don't forget (I think mentioned here or somewhere else) that later AAs allow you to zone/camp with a suspended minion.

KLS
01-14-2010, 08:41 PM
Tell me what data you need to store for the whole implementation and we can carve out a part of the PP for it so you don't need any DB classes. Much of the PP in the DB is unused anyway.

steve
01-15-2010, 01:13 AM
Suspend: Petname tells you, 'By your command, master.' (tell, purple)

Unsuspend: Petname tells you, 'I live again...'(tell, purple)

Suspend with a pet already suspended: You cannot have more than one pet at a time. (red, client message)

Try to unsuspend a pet that isn't there: no message at all

blmille2
01-15-2010, 02:29 AM
Essentially, I need a whole pet structure in the PP, if we want to code it to looking to the future.

If we want to wait until we get to that road later, we can just stick with a very suspended minion pet instance and copy over all the data when suspend minion happens.

It's up to you. <=0D

Assuming we are going to prepare for the future, here's some info:
- Pet information is currently stored in the EPP field and is of type ExtendedProfile_Struct, which looks like the following:

struct ExtendedProfile_Struct {
// Pet stuff
int16 pet_id;
int16 pet_hp;
int16 pet_mana;
SpellBuff_Struct pet_buffs[BUFF_COUNT];
int32 pet_items[MAX_MATERIALS];
char pet_name[64];

uint32 aa_effects;
uint32 perAA; //% of exp going to AAs
};


So, I don't think we need perAA duplicated in the blob field for the suspended minion's stuff, but that's about what we're looking at to store all the pet data.

Thanks!

blmille2
01-15-2010, 04:38 AM
Alright, I now have it exactly how I want it except the pet buffs aren't showing up after the pet is unsuspended.

I do a pet->SendPetBuffsToClient() after I set the buffs back, but I currently have to zone in order to see the pet's buffs.

Any ideas?

Thanks!

blmille2
01-15-2010, 04:57 AM
Okay, so nevermind on the sending pet buffs to client, I was actually doing a GetClient()->SendPetBuffsToClient(), which had no effect.

That's corrected now and I believe this is a good version.

Of course, I know you guys will give me some feedback, which is very welcome!

There is actually a restriction to where you cannot suspend a pet while it is engaged. Therefore, I took the liberty of creating a general message in red text. If anyone finds that message, please relay that back to me or change it yourself if you have access to the code.

Here it is:

Index: zone/AA.h
================================================== =================
--- zone/AA.h (revision 1094)
+++ zone/AA.h (working copy)
@@ -265,6 +265,7 @@
aaParagonofSpirit = 291, //DB
aaAdvancedInnateStrength = 292, //works
aaAdvancedInnateStamina = 302, //works
+ //aaSuspendMinion = 308, //WIP -- brandon
aaAdvancedInnateAgility = 312, //works
aaAdvancedInnateDexterity = 322, //works
aaAdvancedInnateIntelligence = 332, //works
Index: zone/client.cpp
================================================== =================
--- zone/client.cpp (revision 1094)
+++ zone/client.cpp (working copy)
@@ -268,6 +268,7 @@
//for good measure:
memset(&m_pp, 0, sizeof(m_pp));
memset(&m_epp, 0, sizeof(m_epp));
+ memset(&m_suspended_minion, 0, sizeof(m_suspended_minion));
PendingTranslocate = false;
PendingSacrifice = false;
BoatID = 0;
@@ -4767,6 +4768,70 @@
return 0;
}

+void Client::SuspendMinion(){
+ Client *c = this;
+
+ Mob *pet = c->GetPet();
+ NPC *npcPet = NULL;
+
+ int cur_level=c->GetAA(aaID::aaSuspendedMinion);//SuspendedMinion ID
+
+ if(cur_level==0){
+ return;
+ }
+
+ int spell_id=0;
+ if(pet==0){
+ if(m_suspended_minion.pet_id > 0){
+ if(m_suspended_minion.pet_id){
+ c->MakePet(m_suspended_minion.pet_id,spells[m_suspended_minion.pet_id].teleport_zone);
+ pet = GetPet();
+ npcPet = GetPet()->CastToNPC();
+ if(cur_level >= 2){
+ npcPet->SetPetState(m_suspended_minion.pet_buffs, m_suspended_minion.pet_items);
+ pet->SendPetBuffsToClient();
+ }
+ npcPet->CalcBonuses();
+ npcPet->SetHP(m_suspended_minion.pet_hp);
+ npcPet->SetMana(m_suspended_minion.pet_mana);
+ c->Message(clientMessageTell,"%s tells you, 'I live again...'",pet->GetCleanName());
+ memset(&m_suspended_minion, 0, sizeof(m_suspended_minion));
+ }else{
+ c->Message(13,"Error: Couldn't find the spell id for your pet!");
+ return;
+ }
+ }else{
+ return;
+ }
+ }else{
+ npcPet = GetPet()->CastToNPC();
+ spell_id=npcPet->GetPetSpellID();
+ if(spell_id){
+ if(m_suspended_minion.pet_id > 0){
+ c->Message(clientMessageError,"You cannot have more than one pet at a time.");
+ return;
+ }else if(pet->IsEngaged()){
+ c->Message(clientMessageError,"You cannot suspend a pet while in battle.");
+ return;
+ }else{
+
+ m_suspended_minion.pet_id = spell_id;
+ m_suspended_minion.pet_hp = pet->GetHP();;
+ m_suspended_minion.pet_mana = pet->GetMana();
+ if(cur_level >= 2){
+ npcPet->GetPetState(m_suspended_minion.pet_buffs, m_suspended_minion.pet_items,m_suspended_minion.pe t_name);
+ }
+
+ c->Message(clientMessageTell,"%s tells you, 'By your command, master.'",pet->GetCleanName());
+ pet->Kill();
+ }
+ }else{
+ c->Message(13,"Error getting the spell_id from the npc_type id.");
+ return;
+ }
+ }
+}
+
void Client::SummonAndRezzAllCorpses()
{
pendingrezzexp = -1;
Index: zone/client.h
================================================== =================
--- zone/client.h (revision 1094)
+++ zone/client.h (working copy)
@@ -150,6 +150,16 @@
Client *MakeClient(EQStream* ieqs);
};

+struct SuspendedMinion_Struct
+{
+ int16 pet_id;
+ int16 pet_hp;
+ int16 pet_mana;
+ SpellBuff_Struct pet_buffs[BUFF_COUNT];
+ int32 pet_items[MAX_MATERIALS];
+ char pet_name[64];
+};
+
class Client : public Mob
{
public:
@@ -894,6 +904,7 @@
void CalcItemScale();
bool CalcItemScale(int32 slot_x, int32 slot_y);
void SummonAndRezzAllCorpses();
+ void SuspendMinion();
void NotifyNewTitlesAvailable();
void Signal(int32 data);
Mob *GetBindSightTarget() { return bind_sight_target; }
@@ -1033,6 +1044,8 @@
Inventory m_inv;
Object* m_tradeskill_object;

+ SuspendedMinion_Struct m_suspended_minion;
+
AdventureInfo* m_offered_adventure;
AdventureDetails *m_current_adventure;

Index: zone/command.cpp
================================================== =================
--- zone/command.cpp (revision 1094)
+++ zone/command.cpp (working copy)
@@ -341,6 +341,7 @@
command_add("guilds",NULL,0,command_guild) ||
command_add("zonestatus","- Show connected zoneservers, synonymous with /servers",150,command_zonestatus) ||
command_add("manaburn","- Use AA Wizard class skill manaburn on target",10,command_manaburn) ||
+ command_add("suspendminion","- Use pet class AA skill Suspend Minion",10,command_suspendminion) ||
command_add("viewmessage","[id] - View messages in your tell queue",100,command_viewmessage) ||
command_add("viewmessages",NULL,0,command_viewmessage) ||
command_add("doanim","[animnum] [type] - Send an EmoteAnim for you or your target",50,command_doanim) ||
@@ -5158,6 +5159,11 @@
}
}

+void command_suspendminion(Client *c, const Seperator *sep)
+{
+ c->SuspendMinion();
+}
+
void command_viewmessage(Client *c, const Seperator *sep)
{
char errbuf[MYSQL_ERRMSG_SIZE];
Index: zone/command.h
================================================== =================
--- zone/command.h (revision 1094)
+++ zone/command.h (working copy)
@@ -218,6 +218,7 @@
bool helper_guild_edit(Client *c, int32 dbid, int32 eqid, int8 rank, const char* what, const char* value);
void command_zonestatus(Client *c, const Seperator *sep);
void command_manaburn(Client *c, const Seperator *sep);
+void command_suspendminion(Client *c, const Seperator *sep);
void command_viewmessage(Client *c, const Seperator *sep);
void command_doanim(Client *c, const Seperator *sep);
void command_randomfeatures(Client *c, const Seperator *sep);
Index: zone/pets.cpp
================================================== =================
--- zone/pets.cpp (revision 1094)
+++ zone/pets.cpp (working copy)
@@ -213,8 +213,6 @@
return base_hp;
}
*/
-
-
void Mob::MakePet(int16 spell_id, const char* pettype, const char *petname) {
//see if we are a special type of pet (for command filtering)
PetType type = petOther;
Index: zone/spell_effects.cpp
================================================== =================
--- zone/spell_effects.cpp (revision 1094)
+++ zone/spell_effects.cpp (working copy)
@@ -2732,6 +2732,11 @@
CastToClient()->GoToBind(4);
break;
}
+ case SE_SuspendMinion:
+ case SE_SuspendPet:
+ {
+ CastToClient()->SuspendMinion();
+ }

case SE_ImprovedDamage:
case SE_ImprovedHeal:

steve
01-15-2010, 12:05 PM
Pet attacking while trying to suspend:
Your pet is the focus of something's attention. (color of spells msg set in prefs)

Also, I've never seen this message before, because I never try to suspend when in combat. But it also gave me:
Your pet must be a peace, first. (color of spells msg set in prefs)

I think the last one, pet was agro on the mob, wasn't swinging at that precise second but was instead procing a spell.

blmille2
01-16-2010, 02:12 AM
I'll code it to be that if the pet is on any hate list that you can't suspend him.

I could resort to looking to see if he's at the top of the list.

Let me know how live works.

Thanks so much for doing the homework!

blmille2
01-16-2010, 02:47 AM
Okay, so here it is!

Suspend Minion functionality:
- Rank 1 = ability to suspend minion WITHOUT spells OR equipment
- Rank 2 = ability to suspend minion WITH spells AND equipment

Limitations:
- Pet must not be fighting something
- Pet must not be on any aggro list
- Suspended minion will poof upon leaving zone by any means (i.e. zone/log/etc.)
- You cannot suspend more than one minion
- You cannot unsuspend if you have a pet active.


Index: zone/AA.h
================================================== =================
--- zone/AA.h (revision 1094)
+++ zone/AA.h (working copy)
@@ -265,6 +265,7 @@
aaParagonofSpirit = 291, //DB
aaAdvancedInnateStrength = 292, //works
aaAdvancedInnateStamina = 302, //works
+ //aaSuspendMinion = 308, //WIP -- brandon
aaAdvancedInnateAgility = 312, //works
aaAdvancedInnateDexterity = 322, //works
aaAdvancedInnateIntelligence = 332, //works
Index: zone/aggro.cpp
================================================== =================
--- zone/aggro.cpp (revision 1094)
+++ zone/aggro.cpp (working copy)
@@ -31,6 +31,20 @@
extern Zone* zone;
//#define LOSDEBUG 6

+bool Mob::HasAggro(){
+ bool hasAggro = false;
+
+ list<NPC*> npc_list = entity_list.GetNPCList();
+ list<NPC*>::iterator iter;
+ iter = npc_list.begin();
+ while(iter != npc_list.end() && !hasAggro)
+ {
+ hasAggro |= (*iter)->CheckAggro(this);
+ iter++;
+ }
+ return hasAggro;
+}
+
//look around a client for things which might aggro the client.
void EntityList::CheckClientAggro(Client *around) {
_ZP(EntityList_CheckClientAggro);
Index: zone/client.cpp
================================================== =================
--- zone/client.cpp (revision 1094)
+++ zone/client.cpp (working copy)
@@ -268,6 +268,7 @@
//for good measure:
memset(&m_pp, 0, sizeof(m_pp));
memset(&m_epp, 0, sizeof(m_epp));
+ memset(&m_suspended_minion, 0, sizeof(m_suspended_minion));
PendingTranslocate = false;
PendingSacrifice = false;
BoatID = 0;
@@ -4767,6 +4768,72 @@
return 0;
}

+void Client::SuspendMinion(){
+ Client *c = this;
+
+ Mob *pet = c->GetPet();
+ NPC *npcPet = NULL;
+
+ int cur_level=c->GetAA(aaID::aaSuspendedMinion);//SuspendedMinion ID
+
+ if(cur_level==0){
+ return;
+ }
+
+ int spell_id=0;
+ if(pet==0){
+ if(m_suspended_minion.pet_id > 0){
+ if(m_suspended_minion.pet_id){
+ c->MakePet(m_suspended_minion.pet_id,spells[m_suspended_minion.pet_id].teleport_zone);
+ pet = GetPet();
+ npcPet = GetPet()->CastToNPC();
+ if(cur_level >= 2){
+ npcPet->SetPetState(m_suspended_minion.pet_buffs, m_suspended_minion.pet_items);
+ pet->SendPetBuffsToClient();
+ }
+ npcPet->CalcBonuses();
+ npcPet->SetHP(m_suspended_minion.pet_hp);
+ npcPet->SetMana(m_suspended_minion.pet_mana);
+ c->Message(clientMessageTell,"%s tells you, 'I live again...'",pet->GetCleanName());
+ memset(&m_suspended_minion, 0, sizeof(m_suspended_minion));
+ }else{
+ c->Message(13,"Error: Couldn't find the spell id for your pet!");
+ return;
+ }
+ }else{
+ return;
+ }
+ }else{
+ npcPet = GetPet()->CastToNPC();
+ spell_id=npcPet->GetPetSpellID();
+ if(spell_id){
+ if(m_suspended_minion.pet_id > 0){
+ c->Message(clientMessageError,"You cannot have more than one pet at a time.");
+ return;
+ }else if(pet->IsEngaged()){
+ c->Message(clientMessageError,"Your pet must be a peace, first.");
+ return;
+ }else if(pet->HasAggro()){ //If the pet is on any aggro list
+ c->Message(clientMessageBlue,"Your pet is the focus of something's attention.");
+ }else{
+
+ m_suspended_minion.pet_id = spell_id;
+ m_suspended_minion.pet_hp = pet->GetHP();;
+ m_suspended_minion.pet_mana = pet->GetMana();
+ if(cur_level >= 2){
+ npcPet->GetPetState(m_suspended_minion.pet_buffs, m_suspended_minion.pet_items,m_suspended_minion.pe t_name);
+ }
+
+ c->Message(clientMessageTell,"%s tells you, 'By your command, master.'",pet->GetCleanName());
+ pet->Kill();
+ }
+ }else{
+ c->Message(13,"Error getting the spell_id from the npc_type id.");
+ return;
+ }
+ }
+}
+
void Client::SummonAndRezzAllCorpses()
{
pendingrezzexp = -1;
Index: zone/client.h
================================================== =================
--- zone/client.h (revision 1094)
+++ zone/client.h (working copy)
@@ -150,6 +150,16 @@
Client *MakeClient(EQStream* ieqs);
};

+struct SuspendedMinion_Struct
+{
+ int16 pet_id;
+ int16 pet_hp;
+ int16 pet_mana;
+ SpellBuff_Struct pet_buffs[BUFF_COUNT];
+ int32 pet_items[MAX_MATERIALS];
+ char pet_name[64];
+};
+
class Client : public Mob
{
public:
@@ -894,6 +904,7 @@
void CalcItemScale();
bool CalcItemScale(int32 slot_x, int32 slot_y);
void SummonAndRezzAllCorpses();
+ void SuspendMinion();
void NotifyNewTitlesAvailable();
void Signal(int32 data);
Mob *GetBindSightTarget() { return bind_sight_target; }
@@ -1033,6 +1044,8 @@
Inventory m_inv;
Object* m_tradeskill_object;

+ SuspendedMinion_Struct m_suspended_minion;
+
AdventureInfo* m_offered_adventure;
AdventureDetails *m_current_adventure;

Index: zone/command.cpp
================================================== =================
--- zone/command.cpp (revision 1094)
+++ zone/command.cpp (working copy)
@@ -341,6 +341,7 @@
command_add("guilds",NULL,0,command_guild) ||
command_add("zonestatus","- Show connected zoneservers, synonymous with /servers",150,command_zonestatus) ||
command_add("manaburn","- Use AA Wizard class skill manaburn on target",10,command_manaburn) ||
+ command_add("suspendminion","- Use pet class AA skill Suspend Minion",10,command_suspendminion) ||
command_add("viewmessage","[id] - View messages in your tell queue",100,command_viewmessage) ||
command_add("viewmessages",NULL,0,command_viewmessage) ||
command_add("doanim","[animnum] [type] - Send an EmoteAnim for you or your target",50,command_doanim) ||
@@ -5158,6 +5159,11 @@
}
}

+void command_suspendminion(Client *c, const Seperator *sep)
+{
+ c->SuspendMinion();
+}
+
void command_viewmessage(Client *c, const Seperator *sep)
{
char errbuf[MYSQL_ERRMSG_SIZE];
Index: zone/command.h
================================================== =================
--- zone/command.h (revision 1094)
+++ zone/command.h (working copy)
@@ -218,6 +218,7 @@
bool helper_guild_edit(Client *c, int32 dbid, int32 eqid, int8 rank, const char* what, const char* value);
void command_zonestatus(Client *c, const Seperator *sep);
void command_manaburn(Client *c, const Seperator *sep);
+void command_suspendminion(Client *c, const Seperator *sep);
void command_viewmessage(Client *c, const Seperator *sep);
void command_doanim(Client *c, const Seperator *sep);
void command_randomfeatures(Client *c, const Seperator *sep);
Index: zone/mob.h
================================================== =================
--- zone/mob.h (revision 1094)
+++ zone/mob.h (working copy)
@@ -841,6 +841,7 @@
void CheckFlee();

inline bool CheckAggro(Mob* other) {return hate_list.IsOnHateList(other);}
+ bool HasAggro();
float CalculateHeadingToTarget(float in_x, float in_y);
bool CalculateNewPosition(float x, float y, float z, float speed, bool checkZ = false);
bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = true);
Index: zone/pets.cpp
================================================== =================
--- zone/pets.cpp (revision 1094)
+++ zone/pets.cpp (working copy)
@@ -213,8 +213,6 @@
return base_hp;
}
*/
-
-
void Mob::MakePet(int16 spell_id, const char* pettype, const char *petname) {
//see if we are a special type of pet (for command filtering)
PetType type = petOther;
Index: zone/spell_effects.cpp
================================================== =================
--- zone/spell_effects.cpp (revision 1094)
+++ zone/spell_effects.cpp (working copy)
@@ -2732,6 +2732,11 @@
CastToClient()->GoToBind(4);
break;
}
+ case SE_SuspendMinion:
+ case SE_SuspendPet:
+ {
+ CastToClient()->SuspendMinion();
+ }

case SE_ImprovedDamage:
case SE_ImprovedHeal:

demonstar55
01-16-2010, 03:51 AM
I recommend using StringIDs, no idea if the messages are in zone/StringIDs.h though, they're simple to add though, just look them up in your EQ root folder in eqstr_us.txt, I think, might be the en one though, I always forget!

blmille2
01-16-2010, 05:42 AM
I can't find any eqstr_us.txt. I found the stringids.h, but have yet to find the file the messages are actually stored in.

demonstar55
01-16-2010, 06:02 AM
I can't find any eqstr_us.txt. I found the stringids.h, but have yet to find the file the messages are actually stored in.

I said EQ root folder, as in where your game is installed

blmille2
01-16-2010, 06:14 AM
Code:


Index: zone/AA.h
================================================== =================
--- zone/AA.h (revision 1094)
+++ zone/AA.h (working copy)
@@ -265,6 +265,7 @@
aaParagonofSpirit = 291, //DB
aaAdvancedInnateStrength = 292, //works
aaAdvancedInnateStamina = 302, //works
+ //aaSuspendMinion = 308, //WIP -- brandon
aaAdvancedInnateAgility = 312, //works
aaAdvancedInnateDexterity = 322, //works
aaAdvancedInnateIntelligence = 332, //works
Index: zone/aggro.cpp
================================================== =================
--- zone/aggro.cpp (revision 1094)
+++ zone/aggro.cpp (working copy)
@@ -31,6 +31,20 @@
extern Zone* zone;
//#define LOSDEBUG 6

+bool Mob::HasAggro(){
+ bool hasAggro = false;
+
+ list<NPC*> npc_list = entity_list.GetNPCList();
+ list<NPC*>::iterator iter;
+ iter = npc_list.begin();
+ while(iter != npc_list.end() && !hasAggro)
+ {
+ hasAggro |= (*iter)->CheckAggro(this);
+ iter++;
+ }
+ return hasAggro;
+}
+
//look around a client for things which might aggro the client.
void EntityList::CheckClientAggro(Client *around) {
_ZP(EntityList_CheckClientAggro);
Index: zone/client.cpp
================================================== =================
--- zone/client.cpp (revision 1094)
+++ zone/client.cpp (working copy)
@@ -268,6 +268,7 @@
//for good measure:
memset(&m_pp, 0, sizeof(m_pp));
memset(&m_epp, 0, sizeof(m_epp));
+ memset(&m_suspended_minion, 0, sizeof(m_suspended_minion));
PendingTranslocate = false;
PendingSacrifice = false;
BoatID = 0;
@@ -4767,6 +4768,74 @@
return 0;
}

+void Client::SuspendMinion(){
+ Client *c = this;
+
+ Mob *pet = c->GetPet();
+ NPC *npcPet = NULL;
+
+ int cur_level=c->GetAA(aaID::aaSuspendedMinion);//SuspendedMinion ID
+
+ if(cur_level==0){
+ return;
+ }
+
+ int spell_id=0;
+ if(pet==0){
+ if(m_suspended_minion.pet_id > 0){
+ if(m_suspended_minion.pet_id){
+ c->MakePet(m_suspended_minion.pet_id,spells[m_suspended_minion.pet_id].teleport_zone);
+ pet = GetPet();
+ npcPet = GetPet()->CastToNPC();
+ if(cur_level >= 2){
+ npcPet->SetPetState(m_suspended_minion.pet_buffs, m_suspended_minion.pet_items);
+ pet->SendPetBuffsToClient();
+ }
+ npcPet->CalcBonuses();
+ npcPet->SetHP(m_suspended_minion.pet_hp);
+ npcPet->SetMana(m_suspended_minion.pet_mana);
+
+ //c->Message(clientMessageTell,"%s tells you, 'I live again...'",pet->GetCleanName());
+ c->Message_StringID(clientMessageTell,SUSPEND_MINION_ UNSUSPEND,pet->GetCleanName());
+
+ memset(&m_suspended_minion, 0, sizeof(m_suspended_minion));
+ }else{
+ c->Message(13,"Error: Couldn't find the spell id for your pet!");
+ return;
+ }
+ }else{
+ return;
+ }
+ }else{
+ npcPet = GetPet()->CastToNPC();
+ spell_id=npcPet->GetPetSpellID();
+ if(spell_id){
+ if(m_suspended_minion.pet_id > 0){
+ c->Message_StringID(clientMessageError,ONLY_ONE_PET);
+ return;
+ }else if(pet->IsEngaged()){
+ c->Message_StringID(clientMessageError,SUSPEND_MINION _FIGHTING);
+ return;
+ }else if(pet->HasAggro()){ //If the pet is on any aggro list
+ c->Message_StringID(clientMessageBlue,SUSPEND_MINION_ HAS_AGGRO);
+ }else{
+
+ m_suspended_minion.pet_id = spell_id;
+ m_suspended_minion.pet_hp = pet->GetHP();;
+ m_suspended_minion.pet_mana = pet->GetMana();
+ if(cur_level >= 2){
+ npcPet->GetPetState(m_suspended_minion.pet_buffs, m_suspended_minion.pet_items,m_suspended_minion.pe t_name);
+ }
+ c->Message_StringID(clientMessageTell,SUSPEND_MINION_ SUSPEND,pet->GetCleanName());
+ pet->Kill();
+ }
+ }else{
+ c->Message(13,"Error getting the spell_id from the npc_type id.");
+ return;
+ }
+ }
+}
+
void Client::SummonAndRezzAllCorpses()
{
pendingrezzexp = -1;
Index: zone/client.h
================================================== =================
--- zone/client.h (revision 1094)
+++ zone/client.h (working copy)
@@ -150,6 +150,16 @@
Client *MakeClient(EQStream* ieqs);
};

+struct SuspendedMinion_Struct
+{
+ int16 pet_id;
+ int16 pet_hp;
+ int16 pet_mana;
+ SpellBuff_Struct pet_buffs[BUFF_COUNT];
+ int32 pet_items[MAX_MATERIALS];
+ char pet_name[64];
+};
+
class Client : public Mob
{
public:
@@ -894,6 +904,7 @@
void CalcItemScale();
bool CalcItemScale(int32 slot_x, int32 slot_y);
void SummonAndRezzAllCorpses();
+ void SuspendMinion();
void NotifyNewTitlesAvailable();
void Signal(int32 data);
Mob *GetBindSightTarget() { return bind_sight_target; }
@@ -1033,6 +1044,8 @@
Inventory m_inv;
Object* m_tradeskill_object;

+ SuspendedMinion_Struct m_suspended_minion;
+
AdventureInfo* m_offered_adventure;
AdventureDetails *m_current_adventure;

Index: zone/command.cpp
================================================== =================
--- zone/command.cpp (revision 1094)
+++ zone/command.cpp (working copy)
@@ -341,6 +341,7 @@
command_add("guilds",NULL,0,command_guild) ||
command_add("zonestatus","- Show connected zoneservers, synonymous with /servers",150,command_zonestatus) ||
command_add("manaburn","- Use AA Wizard class skill manaburn on target",10,command_manaburn) ||
+ command_add("suspendminion","- Use pet class AA skill Suspend Minion",10,command_suspendminion) ||
command_add("viewmessage","[id] - View messages in your tell queue",100,command_viewmessage) ||
command_add("viewmessages",NULL,0,command_viewmessage) ||
command_add("doanim","[animnum] [type] - Send an EmoteAnim for you or your target",50,command_doanim) ||
@@ -5158,6 +5159,11 @@
}
}

+void command_suspendminion(Client *c, const Seperator *sep)
+{
+ c->SuspendMinion();
+}
+
void command_viewmessage(Client *c, const Seperator *sep)
{
char errbuf[MYSQL_ERRMSG_SIZE];
Index: zone/command.h
================================================== =================
--- zone/command.h (revision 1094)
+++ zone/command.h (working copy)
@@ -218,6 +218,7 @@
bool helper_guild_edit(Client *c, int32 dbid, int32 eqid, int8 rank, const char* what, const char* value);
void command_zonestatus(Client *c, const Seperator *sep);
void command_manaburn(Client *c, const Seperator *sep);
+void command_suspendminion(Client *c, const Seperator *sep);
void command_viewmessage(Client *c, const Seperator *sep);
void command_doanim(Client *c, const Seperator *sep);
void command_randomfeatures(Client *c, const Seperator *sep);
Index: zone/mob.h
================================================== =================
--- zone/mob.h (revision 1094)
+++ zone/mob.h (working copy)
@@ -841,6 +841,7 @@
void CheckFlee();

inline bool CheckAggro(Mob* other) {return hate_list.IsOnHateList(other);}
+ bool HasAggro();
float CalculateHeadingToTarget(float in_x, float in_y);
bool CalculateNewPosition(float x, float y, float z, float speed, bool checkZ = false);
bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = true);
Index: zone/pets.cpp
================================================== =================
--- zone/pets.cpp (revision 1094)
+++ zone/pets.cpp (working copy)
@@ -213,8 +213,6 @@
return base_hp;
}
*/
-
-
void Mob::MakePet(int16 spell_id, const char* pettype, const char *petname) {
//see if we are a special type of pet (for command filtering)
PetType type = petOther;
Index: zone/spell_effects.cpp
================================================== =================
--- zone/spell_effects.cpp (revision 1094)
+++ zone/spell_effects.cpp (working copy)
@@ -2732,6 +2732,11 @@
CastToClient()->GoToBind(4);
break;
}
+ case SE_SuspendMinion:
+ case SE_SuspendPet:
+ {
+ CastToClient()->SuspendMinion();
+ }

case SE_ImprovedDamage:
case SE_ImprovedHeal:
Index: zone/StringIDs.h
================================================== =================
--- zone/StringIDs.h (revision 1094)
+++ zone/StringIDs.h (working copy)
@@ -66,6 +66,7 @@
#define ONLY_ONE_PET 246 //You cannot have more than one pet at a time.
#define CANNOT_CHARM_YET 248 //Your target is too high of a level for your charm spell.
#define NO_PET 255 //You do not have a pet.
+#define SUSPEND_MINION_HAS_AGGRO 256 //Your pet is the focus of something's attention.
#define CORPSE_CANT_SENSE 262 //You cannot sense any corpses for this PC in this zone.
#define SPELL_NO_HOLD 263 //Your spell did not take hold.
#define CANNOT_CHARM 267 //This NPC cannot be charmed.
@@ -184,6 +185,9 @@
#define DOORS_SUCCESSFUL_PICK 1457 //You successfully picked the lock.
#define PLAYER_CHARMED 1461 //You lose control of yourself!
#define TRADER_BUSY 1468 //That Trader is currently with a customer. Please wait until their transaction is finished.
+#define SUSPEND_MINION_UNSUSPEND 3267 //%1 tells you, 'I live again...'
+#define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.'
+#define SUSPEND_MINION_FIGHTING 3270 //Your pet must be at peace, first.
#define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters.
#define PETITION_NO_DELETE 5053 //You do not have a petition in the queue.
#define PETITION_DELETED 5054 //Your petition was successfully deleted.

steve
01-16-2010, 12:16 PM
Awesome. :)

Think you could do the next step?

Persistent Minion: This ability upgrades your Suspended Minion ability to allow pets to remain suspended across zone lines. Also, if you camp with a suspended pet and have this ability, the pet will be available to you when you return.

Live also just recently implemented a change so that you can camp without suspending a pet and it will be there when you get back, without even needing the AA.

blmille2
01-16-2010, 12:40 PM
Persistent Minion requires that we store the minion information to the database somewhere in the player profile. There are already some devs working on where that should go in the near future.

I forgot that there are servers that have a max level above PEQ, so I can see where this would benefit players on those servers.

But, the good news is it looks like it should be pretty simple to extend once there is some free room in the profile for the suspended minion data.

blmille2
01-18-2010, 02:36 PM
Bump.

Are we going to check in this code?

Thanks!

Derision
01-18-2010, 03:31 PM
I'll get to it, probably at the weekend when I have more time to test it, unless another Dev has time to do it before then.

KLS
01-18-2010, 04:38 PM
//WIP -- brandon

I know you see names in comments all over the code but please don't add any more. They don't really add anything and are basically graffiti inside the code.

I'm taking them out where I can find them, and if I ever get around to posting code standards no names will be in there.

Other than that I think it's to the acceptable point now.

Derision
01-19-2010, 02:00 PM
I've committed this in Rev1123.

I put the SuspendedMinion struct into the Player Profile, and also implemented Persistent Minion as it was a simple two line addition to do so.

There was an existing method, EntityList::Fighting which appeared to have the same functionality as your HasAggro method, so I used this.

Most of the other changes I made were purely cosmetic.

I tested it using a Necro ... let me know if I broke anything in the process of committing it.

Finally, nice work, thanks for the contribution, I'm sure the classes who can use this AA will appreciate it.