diff --git a/.gitignore b/.gitignore index 52dbcb224..d5f51e59c 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,7 @@ Build_64/ build_64/ log/ logs/ + +# Patch files +*.orig +*.rej diff --git a/zone/MobAI.cpp b/zone/MobAI.cpp index 5aaf7e04d..00007a0f8 100644 --- a/zone/MobAI.cpp +++ b/zone/MobAI.cpp @@ -1862,12 +1862,12 @@ bool NPC::AI_IdleCastCheck() { void Mob::StartEnrage() { // dont continue if already enraged - if (bEnraged) + if (bEnraged || bInfuriated) return; if (SpecAttackTimers[SPECATK_ENRAGE] && !SpecAttackTimers[SPECATK_ENRAGE]->Check()) return; // see if NPC has possibility to enrage - if (!SpecAttacks[SPECATK_ENRAGE]) + if (!SpecAttacks[SPECATK_ENRAGE] && !SpecAttacks[SPECATK_INFURIATE]) return; // check if timer exists (should be true at all times) if (SpecAttackTimers[SPECATK_ENRAGE]) @@ -1882,16 +1882,25 @@ void Mob::StartEnrage() } // start the timer. need to call IsEnraged frequently since we dont have callback timers :-/ SpecAttackTimers[SPECATK_ENRAGE]->Start(); - bEnraged = true; - entity_list.MessageClose_StringID(this, true, 200, MT_NPCEnrage, NPC_ENRAGE_START, GetCleanName()); + if (SpecAttacks[SPECATK_ENRAGE]) { + bEnraged = true; + entity_list.MessageClose_StringID(this, true, 200, MT_NPCEnrage, NPC_ENRAGE_START, GetCleanName()); + } else if (SpecAttacks[SPECATK_INFURIATE]) { + bInfuriated = true; + entity_list.MessageClose(this, true, 200, MT_NPCEnrage, "%1 is infuriated.", GetCleanName()); + } } void Mob::ProcessEnrage(){ - if(IsEnraged()){ + if(IsEnraged() || IsInfuriated()){ if(SpecAttackTimers[SPECATK_ENRAGE] && SpecAttackTimers[SPECATK_ENRAGE]->Check()){ - entity_list.MessageClose_StringID(this, true, 200, MT_NPCEnrage, NPC_ENRAGE_END, GetCleanName()); SpecAttackTimers[SPECATK_ENRAGE]->Start(EnragedTimer); - bEnraged = false; + if(IsEnraged()) { + entity_list.MessageClose_StringID(this, true, 200, MT_NPCEnrage, NPC_ENRAGE_END, GetCleanName()); + bEnraged = false; + } else if(IsInfuriated()) { + entity_list.MessageClose(this, true, 200, MT_NPCEnrage,"%1 is no longer infuriated.", GetCleanName()); + bInfuriated = false; } } } } @@ -1901,6 +1910,11 @@ bool Mob::IsEnraged() return bEnraged; } +bool Mob::IsInfuriated() +{ + return bInfuriated; +} + bool Mob::Flurry() { // this is wrong, flurry is extra attacks on the current target diff --git a/zone/attack.cpp b/zone/attack.cpp index 170ed0fe7..8f5805810 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -382,6 +382,10 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) damage = -3; mlog(COMBAT__DAMAGE, "I am enraged, riposting frontal attack."); } + if (IsInfuriated()) { + damage = -3; + mlog(COMBAT__DAMAGE, "I am infuriated, riposting attacks from all sides."); + } ///////////////////////////////////////////////////////// // riposte diff --git a/zone/bot.cpp b/zone/bot.cpp index f6bdd6667..68ddbc38e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3752,6 +3752,9 @@ void Bot::AI_Process() { // Stop attacking if the target is enraged if(IsEngaged() && !BehindMob(GetTarget(), GetX(), GetY()) && GetTarget()->IsEnraged()) return; + // Stop attacking if the target is infuriated + if(IsEngaged() && GetTarget()->IsInfuriated()) + return; if(GetBotStance() == BotStancePassive) return; @@ -4080,6 +4083,9 @@ void Bot::PetAIProcess() { // Stop attacking while we are on a front arc and the target is enraged if(!botPet->BehindMob(botPet->GetTarget(), botPet->GetX(), botPet->GetY()) && botPet->GetTarget()->IsEnraged()) return; + // Stop attacking while the target is infuriated + if(botPet->GetTarget()->IsInfuriated()) + return; if(botPet->Attack(GetTarget(), SLOT_PRIMARY)) // try the main hand if (botPet->GetTarget()) // Do we still have a target? @@ -7842,6 +7848,11 @@ bool Bot::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) damage = -3; mlog(COMBAT__DAMAGE, "I am enraged, riposting frontal attack."); } + // same as enrage just no behind check needed. + if (IsInfuriated()) { + damage = -3; + mlog(COMBAT__DAMAGE, "I am infuriated, riposting attacks from all sides."); + } ///////////////////////////////////////////////////////// // riposte diff --git a/zone/common.h b/zone/common.h index 1d1aaffba..26b73c258 100644 --- a/zone/common.h +++ b/zone/common.h @@ -84,11 +84,11 @@ typedef enum { //focus types /* Used: -b,d,f,g,j,m,n,o,p,r,t +b,d,e,f,g,j,m,n,o,p,r,t A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,Q,R,S,T,U,W,Y Unused: -a,c,e,h,k,l,q,s,u,v,w,x,y,z +a,c,h,k,l,q,s,u,v,w,x,y,z P,V,X */ @@ -96,6 +96,7 @@ enum { SPECATK_NONE = 0, SPECATK_SUMMON, // S SPECATK_ENRAGE, // E + SPECATK_INFURIATE, // e SPECATK_RAMPAGE, // R SPECATK_AREA_RAMPAGE, // r SPECATK_FLURRY, // F diff --git a/zone/merc.cpp b/zone/merc.cpp index 6f7c8142f..68923ed8e 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1651,6 +1651,9 @@ void Merc::AI_Process() { // Stop attacking if the target is enraged if(IsEngaged() && !BehindMob(GetTarget(), GetX(), GetY()) && GetTarget()->IsEnraged()) return; + // stop attacking if the target is infuriated + if(IsEngaged() && GetTarget()->IsInfuriated()) + return; //TODO: Implement Stances. /*if(GetBotStance() == BotStancePassive) return;*/ diff --git a/zone/mob.h b/zone/mob.h index 3c3767842..dd7d3933e 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -648,6 +648,7 @@ public: void StartEnrage(); void ProcessEnrage(); bool IsEnraged(); + bool IsInfuriated(); void Taunt(NPC* who, bool always_succeed, float chance_bonus = 0); virtual void AI_Init(); @@ -945,6 +946,7 @@ protected: char lastname[64]; bool bEnraged; + bool bInfuriated; Timer *SpecAttackTimers[SPECATK_MAXNUM]; bool destructibleobject; diff --git a/zone/npc.cpp b/zone/npc.cpp index cc1aa8603..997a72d28 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1447,6 +1447,9 @@ void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool rem case 'E': SpecAttacks[SPECATK_ENRAGE] = (remove ? false : true); break; + case 'e': + SpecAttacks[SPECATK_INFURIATE] = (remove ? false : true); + break; case 'F': SpecAttacks[SPECATK_FLURRY] = (remove ? false : true); break; @@ -1592,6 +1595,10 @@ bool Mob::HasNPCSpecialAtk(const char* parse) { if (!SpecAttacks[SPECATK_ENRAGE]) HasAllAttacks = false; break; + case 'e': + if (!SpecAttacks[SPECATK_INFURIATE]) + HasAllAttacks = false; + break; case 'F': if (!SpecAttacks[SPECATK_FLURRY]) HasAllAttacks = false; diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index e40365faf..4afd16e98 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -5010,6 +5010,32 @@ XS(XS_Mob_IsEnraged) XSRETURN(1); } +XS(XS_Mob_IsInfuriated); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsInfuriated) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsInfuriated(THIS)"); + { + Mob * THIS; + bool RETVAL; + + if (sv_derived_from(ST(0), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Mob"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsInfuriated(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + XS(XS_Mob_GetReverseFactionCon); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_GetReverseFactionCon) { @@ -8267,6 +8293,7 @@ XS(boot_Mob) newXSproto(strcpy(buf, "IsStunned"), XS_Mob_IsStunned, file, "$"); newXSproto(strcpy(buf, "StartEnrage"), XS_Mob_StartEnrage, file, "$"); newXSproto(strcpy(buf, "IsEnraged"), XS_Mob_IsEnraged, file, "$"); + newXSproto(strcpy(buf, "IsInfuriated"), XS_Mob_IsInfuriated, file, "$"); newXSproto(strcpy(buf, "GetReverseFactionCon"), XS_Mob_GetReverseFactionCon, file, "$$"); newXSproto(strcpy(buf, "IsAIControlled"), XS_Mob_IsAIControlled, file, "$"); newXSproto(strcpy(buf, "GetAggroRange"), XS_Mob_GetAggroRange, file, "$");