Merge pull request #292 from KayenEQ/Development

Updated swarm pet AI to be consistent with live.
This commit is contained in:
Michael Cook (mackal)
2014-11-18 15:04:08 -05:00
16 changed files with 210 additions and 94 deletions
+16 -14
View File
@@ -542,10 +542,7 @@ void Client::HandleAAAction(aaID activate) {
}
}
//Originally written by Branks
//functionality rewritten by Father Nitwit
void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, uint32 duration_override) {
void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, uint32 duration_override, bool followme, bool sticktarg) {
//It might not be a bad idea to put these into the database, eventually..
@@ -563,7 +560,7 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
pet.count = 1;
pet.duration = 1;
for(int x = 0; x < 12; x++)
for(int x = 0; x < MAX_SWARM_PETS; x++)
{
if(spells[spell_id].effectid[x] == SE_TemporaryPets)
{
@@ -607,8 +604,6 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
static const float swarm_pet_y[MAX_SWARM_PETS] = { 5, 5, -5, -5,
10, 10, -10, -10,
8, 8, -8, -8 };
TempPets(true);
while(summon_count > 0) {
int pet_duration = pet.duration;
if(duration_override > 0)
@@ -628,7 +623,7 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
GetX()+swarm_pet_x[summon_count], GetY()+swarm_pet_y[summon_count],
GetZ(), GetHeading(), FlyMode3);
if((spell_id == 6882) || (spell_id == 6884))
if (followme)
npca->SetFollowID(GetID());
if(!npca->GetSwarmInfo()){
@@ -646,7 +641,10 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
//give the pets somebody to "love"
if(targ != nullptr){
npca->AddToHateList(targ, 1000, 1000);
npca->GetSwarmInfo()->target = targ->GetID();
if (RuleB(Spells, SwarmPetTargetLock) || sticktarg)
npca->GetSwarmInfo()->target = targ->GetID();
else
npca->GetSwarmInfo()->target = 0;
}
//we allocated a new NPC type object, give the NPC ownership of that memory
@@ -662,7 +660,7 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
targ->AddToHateList(this, 1, 0);
}
void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_override, uint32 duration_override, bool followme) {
void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_override, uint32 duration_override, bool followme, bool sticktarg) {
AA_SwarmPet pet;
pet.count = 1;
@@ -700,7 +698,6 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid
static const float swarm_pet_y[MAX_SWARM_PETS] = { 5, 5, -5, -5,
10, 10, -10, -10,
8, 8, -8, -8 };
TempPets(true);
while(summon_count > 0) {
int pet_duration = pet.duration;
@@ -721,6 +718,9 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid
GetX()+swarm_pet_x[summon_count], GetY()+swarm_pet_y[summon_count],
GetZ(), GetHeading(), FlyMode3);
if (followme)
npca->SetFollowID(GetID());
if(!npca->GetSwarmInfo()){
AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo;
npca->SetSwarmInfo(nSI);
@@ -736,7 +736,11 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid
//give the pets somebody to "love"
if(targ != nullptr){
npca->AddToHateList(targ, 1000, 1000);
npca->GetSwarmInfo()->target = targ->GetID();
if (RuleB(Spells, SwarmPetTargetLock) || sticktarg)
npca->GetSwarmInfo()->target = targ->GetID();
else
npca->GetSwarmInfo()->target = 0;
}
//we allocated a new NPC type object, give the NPC ownership of that memory
@@ -895,8 +899,6 @@ void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration)
make_npc->d_meele_texture1 = 0;
make_npc->d_meele_texture2 = 0;
TempPets(true);
NPC* npca = new NPC(make_npc, 0, GetX(), GetY(), GetZ(), GetHeading(), FlyMode3);
if(!npca->GetSwarmInfo()){
+11
View File
@@ -2062,6 +2062,13 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
}
SetHP(0);
SetPet(0);
if (GetSwarmOwner()){
Mob* owner = entity_list.GetMobID(GetSwarmOwner());
if (owner)
owner->SetTempPetCount(owner->GetTempPetCount() - 1);
}
Mob* killer = GetHateDamageTop(this);
entity_list.RemoveFromTargets(this, p_depop);
@@ -2535,6 +2542,10 @@ void Mob::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp,
if (myowner->IsAIControlled() && !myowner->GetSpecialAbility(IMMUNE_AGGRO))
myowner->hate_list.Add(other, 0, 0, bFrenzy);
}
if (other->GetTempPetCount())
entity_list.AddTempPetsToHateList(other, this, bFrenzy);
if (!wasengaged) {
if(IsNPC() && other->IsClient() && other->CastToClient())
parse->EventNPC(EVENT_AGGRO, this->CastToNPC(), other, "", 0);
-1
View File
@@ -6264,7 +6264,6 @@ void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_overrid
static const float swarm_pet_x[MAX_SWARM_PETS] = { 5, -5, 5, -5, 10, -10, 10, -10, 8, -8, 8, -8 };
static const float swarm_pet_y[MAX_SWARM_PETS] = { 5, 5, -5, -5, 10, 10, -10, -10, 8, 8, -8, -8 };
TempPets(true);
while(summon_count > 0) {
NPCType *npc_dup = nullptr;
+2 -1
View File
@@ -133,7 +133,8 @@ enum {
ALLOW_BENEFICIAL = 38,
DISABLE_MELEE = 39,
NPC_CHASE_DISTANCE = 40,
MAX_SPECIAL_ATTACK = 41
ALLOW_TO_TANK = 41,
MAX_SPECIAL_ATTACK = 42
};
+36
View File
@@ -3622,6 +3622,42 @@ void EntityList::DestroyTempPets(Mob *owner)
}
}
int16 EntityList::CountTempPets(Mob *owner)
{
int16 count = 0;
auto it = npc_list.begin();
while (it != npc_list.end()) {
NPC* n = it->second;
if (n->GetSwarmInfo()) {
if (n->GetSwarmInfo()->owner_id == owner->GetID()) {
count++;
}
}
++it;
}
owner->SetTempPetCount(count);
return count;
}
void EntityList::AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy)
{
if (!other || !owner)
return;
auto it = npc_list.begin();
while (it != npc_list.end()) {
NPC* n = it->second;
if (n->GetSwarmInfo()) {
if (n->GetSwarmInfo()->owner_id == owner->GetID()) {
n->CastToNPC()->hate_list.Add(other, 0, 0, bFrenzy);
}
}
++it;
}
}
bool Entity::CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z,
float trg_x, float trg_y, float trg_z, float perwalk)
{
+2
View File
@@ -251,6 +251,8 @@ public:
void RemoveAllLocalities();
void RemoveAllRaids();
void DestroyTempPets(Mob *owner);
int16 CountTempPets(Mob *owner);
void AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy = false);
Entity *GetEntityMob(uint16 id);
Entity *GetEntityMerc(uint16 id);
Entity *GetEntityDoor(uint16 id);
+7
View File
@@ -403,6 +403,13 @@ Mob *HateList::GetTop(Mob *center)
}
}
if (!isTopClientType) {
if (top->GetSpecialAbility(ALLOW_TO_TANK)){
isTopClientType = true;
topClientTypeInRange = top;
}
}
if(!isTopClientType)
return topClientTypeInRange ? topClientTypeInRange : nullptr;
+3 -2
View File
@@ -362,7 +362,8 @@ Mob::Mob(const char* in_name,
nexthpevent = -1;
nextinchpevent = -1;
TempPets(false);
hasTempPet = false;
count_TempPet = 0;
m_is_running = false;
@@ -423,7 +424,7 @@ Mob::~Mob()
delete trade;
}
if(HadTempPets()){
if(HasTempPetsActive()){
entity_list.DestroyTempPets(this);
}
entity_list.UnMarkNPC(GetID());
+7 -4
View File
@@ -575,8 +575,8 @@ public:
virtual void UnStun();
inline void Silence(bool newval) { silenced = newval; }
inline void Amnesia(bool newval) { amnesiad = newval; }
void TemporaryPets(uint16 spell_id, Mob *target, const char *name_override = nullptr, uint32 duration_override = 0);
void TypesTemporaryPets(uint32 typesid, Mob *target, const char *name_override = nullptr, uint32 duration_override = 0, bool followme = false);
void TemporaryPets(uint16 spell_id, Mob *target, const char *name_override = nullptr, uint32 duration_override = 0, bool followme=true, bool sticktarg=false);
void TypesTemporaryPets(uint32 typesid, Mob *target, const char *name_override = nullptr, uint32 duration_override = 0, bool followme=true, bool sticktarg=false);
void WakeTheDead(uint16 spell_id, Mob *target, uint32 duration);
void Spin();
void Kill();
@@ -673,8 +673,10 @@ public:
inline virtual bool HasOwner() { if(GetOwnerID()==0){return false;} return( entity_list.GetMob(GetOwnerID()) != 0); }
inline virtual bool IsPet() { return(HasOwner() && !IsMerc()); }
inline bool HasPet() const { if(GetPetID()==0){return false;} return (entity_list.GetMob(GetPetID()) != 0);}
bool HadTempPets() const { return(hasTempPet); }
void TempPets(bool i) { hasTempPet = i; }
inline bool HasTempPetsActive() const { return(hasTempPet); }
inline void SetTempPetsActive(bool i) { hasTempPet = i; }
inline int16 GetTempPetCount() const { return count_TempPet; }
inline void SetTempPetCount(int16 i) { count_TempPet = i; }
bool HasPetAffinity() { if (aabonuses.GivePetGroupTarget || itembonuses.GivePetGroupTarget || spellbonuses.GivePetGroupTarget) return true; return false; }
inline bool IsPetOwnerClient() const { return pet_owner_client; }
inline void SetPetOwnerClient(bool value) { pet_owner_client = value; }
@@ -1232,6 +1234,7 @@ protected:
//temppet
bool hasTempPet;
bool _IsTempPet;
int16 count_TempPet;
bool pet_owner_client; //Flags regular and pets as belonging to a client
EGNode *_egnode; //the EG node we are in
+67 -47
View File
@@ -405,8 +405,8 @@ void NPC::SetTarget(Mob* mob) {
if(mob == GetTarget()) //dont bother if they are allready our target
return;
//our target is already set, do not turn from the course, unless our current target is dead.
if(GetSwarmInfo() && GetTarget() && (GetTarget()->GetHP() > 0)) {
//This is not the default behavior for swarm pets, must be specified from quest functions or rules value.
if(GetSwarmInfo() && GetSwarmInfo()->target && GetTarget() && (GetTarget()->GetHP() > 0)) {
Mob *targ = entity_list.GetMob(GetSwarmInfo()->target);
if(targ != mob){
return;
@@ -1841,60 +1841,53 @@ bool Mob::HasNPCSpecialAtk(const char* parse) {
void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
{
Mob::FillSpawnStruct(ns, ForWho);
PetOnSpawn(ns);
ns->spawn.is_npc = 1;
}
void NPC::PetOnSpawn(NewSpawn_Struct* ns)
{
//Basic settings to make sure swarm pets work properly.
if (GetSwarmOwner()) {
Client *c = entity_list.GetClientByID(GetSwarmOwner());
if(c) {
SetAllowBeneficial(1); //Allow client cast swarm pets to be heal/buffed.
SetPetOwnerClient(true);
//This is a hack to allow CLIENT swarm pets NOT to be targeted with F8. Warning: Will turn name 'Yellow'!
if (RuleB(Pets, SwarmPetNotTargetableWithHotKey))
ns->spawn.IsMercenary = 1;
Mob *m = entity_list.GetMobID(GetSwarmOwner());
if(m->IsClient()) {
SetPetOwnerClient(true); //Simple flag to determine if pet belongs to a client
SetAllowBeneficial(1);//Allow temp pets to receive buffs and heals if owner is client.
//This is a hack to allow CLIENT swarm pets NOT to be targeted with F8. Warning: Will turn name 'Yellow'!
if (RuleB(Pets, SwarmPetNotTargetableWithHotKey))
ns->spawn.IsMercenary = 1;
}
//NPC cast swarm pets should still be targetable with F8.
else
ns->spawn.IsMercenary = 0;
}
else
ns->spawn.IsMercenary = 0;
//Not recommended if using above (However, this will work better on older clients).
if (RuleB(Pets, UnTargetableSwarmPet)) {
if(GetOwnerID() || GetSwarmOwner()) {
ns->spawn.is_pet = 1;
if (!IsCharmed() && GetOwnerID()) {
Client *c = entity_list.GetClientByID(GetOwnerID());
if(c){
SetPetOwnerClient(true);
sprintf(ns->spawn.lastName, "%s's Pet", c->GetName());
}
}
else if (GetSwarmOwner()) {
ns->spawn.bodytype = 11;
if(!IsCharmed())
{
Client *c = entity_list.GetClientByID(GetSwarmOwner());
if(c){
SetPetOwnerClient(true);
sprintf(ns->spawn.lastName, "%s's Pet", c->GetName());
}
}
SetTempPet(true); //Simple mob flag for checking if temp pet
m->SetTempPetsActive(true); //Neccessary fail safe flag set if mob ever had a swarm pet to ensure they are removed.
m->SetTempPetCount(m->GetTempPetCount() + 1);
//Not recommended if using above (However, this will work better on older clients).
if (RuleB(Pets, UnTargetableSwarmPet)) {
ns->spawn.bodytype = 11;
if(!IsCharmed() && m->IsClient())
sprintf(ns->spawn.lastName, "%s's Pet", m->GetName());
}
}
else if(GetOwnerID()) {
ns->spawn.is_pet = 1;
if (!IsCharmed() && GetOwnerID()) {
Client *c = entity_list.GetClientByID(GetOwnerID());
if(c){
SetPetOwnerClient(true);
sprintf(ns->spawn.lastName, "%s's Pet", c->GetName());
}
}
} else {
if(GetOwnerID()) {
ns->spawn.is_pet = 1;
if (!IsCharmed() && GetOwnerID()) {
Client *c = entity_list.GetClientByID(GetOwnerID());
if(c){
SetPetOwnerClient(true);
sprintf(ns->spawn.lastName, "%s's Pet", c->GetName());
}
}
} else
ns->spawn.is_pet = 0;
}
}
ns->spawn.is_npc = 1;
else
ns->spawn.is_pet = 0;
}
void NPC::SetLevel(uint8 in_level, bool command)
@@ -2417,3 +2410,30 @@ void NPC::DoQuestPause(Mob *other) {
}
}
void NPC::DepopSwarmPets()
{
if (GetSwarmInfo()) {
if (GetSwarmInfo()->duration->Check(false)){
Mob* owner = entity_list.GetMobID(GetSwarmInfo()->owner_id);
if (owner)
owner->SetTempPetCount(owner->GetTempPetCount() - 1);
Depop();
return;
}
//This is only used for optional quest or rule derived behavior now if you force a temp pet on a specific target.
if (GetSwarmInfo()->target) {
Mob *targMob = entity_list.GetMob(GetSwarmInfo()->target);
if(!targMob || (targMob && targMob->IsCorpse())){
Mob* owner = entity_list.GetMobID(GetSwarmInfo()->owner_id);
if (owner)
owner->SetTempPetCount(owner->GetTempPetCount() - 1);
Depop();
return;
}
}
}
}
+2
View File
@@ -243,6 +243,8 @@ public:
uint32 GetSwarmOwner();
uint32 GetSwarmTarget();
void SetSwarmTarget(int target_id = 0);
void DepopSwarmPets();
void PetOnSpawn(NewSpawn_Struct* ns);
void SignalNPC(int _signal_id);
+33 -14
View File
@@ -1517,14 +1517,15 @@ XS(XS_Mob_MakeTempPet); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_MakeTempPet)
{
dXSARGS;
if (items < 2 || items > 5)
Perl_croak(aTHX_ "Usage: Mob::MakeTempPet(THIS, spell_id, name=nullptr, duration=0, target=nullptr)");
if (items < 2 || items > 6)
Perl_croak(aTHX_ "Usage: Mob::MakeTempPet(THIS, spell_id, name=nullptr, duration=0, target=nullptr, sticktarg=0)");
{
Mob * THIS;
uint16 spell_id = (uint16)SvUV(ST(1));
char * name;
uint32 duration;
Mob * target;
bool sticktarg;
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
@@ -1554,7 +1555,13 @@ XS(XS_Mob_MakeTempPet)
else
Perl_croak(aTHX_ "owner is not of type Mob");
THIS->TemporaryPets(spell_id, target, name, duration);
if (items < 6)
sticktarg = false;
else {
sticktarg = (bool)SvTRUE(ST(5));
}
THIS->TemporaryPets(spell_id, target, name, duration, true, sticktarg);
}
XSRETURN_EMPTY;
}
@@ -1563,15 +1570,16 @@ XS(XS_Mob_TypesTempPet); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_TypesTempPet)
{
dXSARGS;
if (items < 2 || items > 6)
Perl_croak(aTHX_ "Usage: Mob::TypesTempPet(THIS, typesid, name=nullptr, duration=0, target=nullptr, follow=0)");
if (items < 2 || items > 7)
Perl_croak(aTHX_ "Usage: Mob::TypesTempPet(THIS, typesid, name=nullptr, duration=0, follow=0, target=nullptr, sticktarg=0,)");
{
Mob * THIS;
uint32 typesid = (uint32)SvUV(ST(1));
char * name;
uint32 duration;
Mob * target;
bool follow;
Mob * target;
bool sticktarg;
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
@@ -1593,21 +1601,28 @@ XS(XS_Mob_TypesTempPet)
duration = (uint32)SvUV(ST(3));
if (items < 5)
follow = true;
else {
follow = (bool)SvTRUE(ST(4));
}
if (items < 6)
target = nullptr;
else if (sv_derived_from(ST(4), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(4)));
else if (sv_derived_from(ST(5), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(5)));
target = INT2PTR(Mob *,tmp);
}
else
Perl_croak(aTHX_ "target is not of type Mob");
if (items < 6)
follow = false;
if (items < 7)
sticktarg = false;
else {
follow = (bool)SvTRUE(ST(5));
sticktarg = (bool)SvTRUE(ST(6));
}
THIS->TypesTemporaryPets(typesid, target, name, duration, follow);
THIS->TypesTemporaryPets(typesid, target, name, duration, follow, sticktarg);
}
XSRETURN_EMPTY;
}
@@ -3956,7 +3971,10 @@ XS(XS_Mob_CastSpell)
resist_adjust = (int16)SvIV(ST(6));
}
THIS->CastSpell(spell_id, target_id, slot, casttime, mana_cost, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0, &resist_adjust);
if (resist_adjust == 0)//If you do not pass resist adjust as nullptr it will ignore the spells default resist adjust
THIS->CastSpell(spell_id, target_id, slot, casttime, mana_cost, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0);
else
THIS->CastSpell(spell_id, target_id, slot, casttime, mana_cost, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0, &resist_adjust);
}
XSRETURN_EMPTY;
}
@@ -8433,7 +8451,8 @@ XS(boot_Mob)
newXSproto(strcpy(buf, "SetRace"), XS_Mob_SetRace, file, "$$");
newXSproto(strcpy(buf, "SetGender"), XS_Mob_SetGender, file, "$$");
newXSproto(strcpy(buf, "SendIllusion"), XS_Mob_SendIllusion, file, "$$;$$$$$$$$$$$$");
newXSproto(strcpy(buf, "MakeTempPet"), XS_Mob_MakeTempPet, file, "$$;$$$");
newXSproto(strcpy(buf, "MakeTempPet"), XS_Mob_MakeTempPet, file, "$$;$$$$");
newXSproto(strcpy(buf, "TypesTempPet"), XS_Mob_TypesTempPet, file, "$$;$$$$$");
newXSproto(strcpy(buf, "QuestReward"), XS_Mob_QuestReward, file, "$$;$$$");
newXSproto(strcpy(buf, "CameraEffect"), XS_Mob_CameraEffect, file, "$$;$$$");
newXSproto(strcpy(buf, "SpellEffect"), XS_Mob_SpellEffect, file, "$$;$$$$$$");
+7 -11
View File
@@ -122,17 +122,7 @@ void Mob::SpellProcess()
void NPC::SpellProcess()
{
Mob::SpellProcess();
if (GetSwarmInfo()) {
if (GetSwarmInfo()->duration->Check(false))
Depop();
Mob *targMob = entity_list.GetMob(GetSwarmInfo()->target);
if (GetSwarmInfo()->target != 0) {
if(!targMob || (targMob && targMob->IsCorpse()))
Depop();
}
}
DepopSwarmPets();
}
///////////////////////////////////////////////////////////////////////////////
@@ -3349,6 +3339,12 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
sprintf(temp1, "%d", spell_id);
parse->EventNPC(EVENT_CAST_ON, spelltar->CastToNPC(), this, temp1, 0);
}
else if (spelltar->IsClient())
{
char temp1[100];
sprintf(temp1, "%d", spell_id);
parse->EventPlayer(EVENT_CAST_ON, spelltar->CastToClient(),temp1, 0);
}
mod_spell_cast(spell_id, spelltar, reflect, use_resist_adjust, resist_adjust, isproc);