diff --git a/common/ruletypes.h b/common/ruletypes.h index da08cb858..1796c9e4d 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -419,6 +419,8 @@ RULE_BOOL(Combat, UseIntervalAC, true) RULE_INT(Combat, PetAttackMagicLevel, 30) RULE_BOOL(Combat, EnableFearPathing, true) RULE_REAL(Combat, FleeMultiplier, 2.0) // Determines how quickly a NPC will slow down while fleeing. Decrease multiplier to slow NPC down quicker. +RULE_BOOL(Combat, FleeGray, true) // If true FleeGrayHPRatio will be used. +RULE_INT(Combat, FleeGrayHPRatio, 50) //HP % when a Gray NPC begins to flee. RULE_INT(Combat, FleeHPRatio, 25) //HP % when a NPC begins to flee. RULE_BOOL(Combat, FleeIfNotAlone, false) // If false, mobs won't flee if other mobs are in combat with it. RULE_BOOL(Combat, AdjustProcPerMinute, true) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 91d14498f..f8162322e 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -424,7 +424,7 @@ Mob* EntityList::AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAss return nullptr; } -int EntityList::GetHatedCount(Mob *attacker, Mob *exclude) +int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con) { // Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker if (!attacker) @@ -434,20 +434,25 @@ int EntityList::GetHatedCount(Mob *attacker, Mob *exclude) for (auto it = npc_list.begin(); it != npc_list.end(); ++it) { NPC *mob = it->second; - if (!mob || (mob == exclude)) + if (!mob || (mob == exclude)) { continue; + } - if (!mob->IsEngaged()) + if (!mob->IsEngaged()) { continue; + } - if (mob->IsFeared() || mob->IsMezzed()) + if (mob->IsFeared() || mob->IsMezzed()) { continue; + } - if (attacker->GetLevelCon(mob->GetLevel()) == CON_GRAY) + if (!inc_gray_con && attacker->GetLevelCon(mob->GetLevel()) == CON_GRAY) { continue; + } - if (!mob->CheckAggro(attacker)) + if (!mob->CheckAggro(attacker)) { continue; + } float AggroRange = mob->GetAggroRange(); @@ -455,14 +460,12 @@ int EntityList::GetHatedCount(Mob *attacker, Mob *exclude) AggroRange *= AggroRange; - if (DistanceSquared(mob->GetPosition(), attacker->GetPosition()) > AggroRange) + if (DistanceSquared(mob->GetPosition(), attacker->GetPosition()) > AggroRange) { continue; - + } Count++; } - return Count; - } void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { diff --git a/zone/entity.h b/zone/entity.h index f296a8cb4..f32532392 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -416,7 +416,7 @@ public: void CheckClientAggro(Client *around); Mob* AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAssistRange); - int GetHatedCount(Mob *attacker, Mob *exclude); + int GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con); void AIYellForHelp(Mob* sender, Mob* attacker); bool AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes); bool Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes); diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index 0593cbee2..722e1e417 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -31,87 +31,117 @@ extern Zone* zone; //this is called whenever we are damaged to process possible fleeing void Mob::CheckFlee() { - //if were allready fleeing, dont need to check more... - if(flee_mode && currently_fleeing) + + // if mob is dead why would you run? + if(GetHP() == 0) { return; + } + + // if were already fleeing, don't need to check more... + if(flee_mode && currently_fleeing) { + return; + } //dont bother if we are immune to fleeing - if(GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) + if(GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) { return; + } - if(!flee_timer.Check()) - return; //only do all this stuff every little while, since - //its not essential that we start running RIGHT away - - //see if were possibly hurt enough - float ratio = GetHPRatio(); - float fleeratio = GetSpecialAbility(FLEE_PERCENT); - fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio); - - if(ratio >= fleeratio) + // Check if Flee Timer is cleared + if(!flee_timer.Check()) { return; + } - //we might be hurt enough, check con now.. + int hpratio = GetIntHPRatio(); + int fleeratio = GetSpecialAbility(FLEE_PERCENT); // if a special flee_percent exists Mob *hate_top = GetHateTop(); + + // Sanity Check for race conditions + if(hate_top == nullptr) { + return; + } + + // If no special flee_percent check for Gray or Other con rates + if(GetLevelCon(hate_top->GetLevel(), GetLevel()) == CON_GRAY && fleeratio == 0 && RuleB(Combat, FleeGray)) { + fleeratio = RuleI(Combat, FleeGrayHPRatio); + } else if(fleeratio == 0) { + fleeratio = RuleI(Combat, FleeHPRatio ); + } + + // Mob does not have low enough health to flee + if(hpratio >= fleeratio) { + return; + } + + // Sanity Check this should never happen... if(!hate_top) { - //this should never happen... StartFleeing(); return; } - float other_ratio = hate_top->GetHPRatio(); + int other_ratio = hate_top->GetIntHPRatio(); + // If the Client is nearing death the NPC will not flee and instead try to kill the client. if(other_ratio < 20) { - //our hate top is almost dead too... stay and fight return; } - //base our flee ratio on our con. this is how the - //attacker sees the mob, since this is all we can observe + // Flee Chance checking based on con. uint32 con = GetLevelCon(hate_top->GetLevel(), GetLevel()); - float run_ratio; + int flee_chance; switch(con) { //these values are not 100% researched case CON_GRAY: - run_ratio = fleeratio; + flee_chance = 100; break; case CON_GREEN: - run_ratio = fleeratio * 9 / 10; + flee_chance = 90; break; case CON_LIGHTBLUE: - run_ratio = fleeratio * 9 / 10; + flee_chance = 90; break; case CON_BLUE: - run_ratio = fleeratio * 8 / 10; + flee_chance = 80; break; default: - run_ratio = fleeratio * 7 / 10; + flee_chance = 70; break; } - if(ratio < run_ratio) - { - if (RuleB(Combat, FleeIfNotAlone) || - GetSpecialAbility(ALWAYS_FLEE) || - (!RuleB(Combat, FleeIfNotAlone) && (entity_list.GetHatedCount(hate_top, this) == 0))) - StartFleeing(); + + // If we got here we are allowed to roll on flee chance if there is not other hated NPC's in the area. + + if(RuleB(Combat, FleeIfNotAlone) || GetSpecialAbility(ALWAYS_FLEE) || zone->random.Roll(flee_chance) && entity_list.GetHatedCount(hate_top, this, true) == 0) { + currently_fleeing = true; + StartFleeing(); } } + void Mob::ProcessFlee() { //Stop fleeing if effect is applied after they start to run. //When ImmuneToFlee effect fades it will turn fear back on and check if it can still flee. if (flee_mode && (GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) && - !spellbonuses.IsFeared && !spellbonuses.IsBlind) { + !spellbonuses.IsFeared && !spellbonuses.IsBlind) { currently_fleeing = false; return; } - //see if we are still dying, if so, do nothing - float fleeratio = GetSpecialAbility(FLEE_PERCENT); - fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio); - if (GetHPRatio() < fleeratio) + int hpratio = GetIntHPRatio(); + int fleeratio = GetSpecialAbility(FLEE_PERCENT); // if a special flee_percent exists + Mob *hate_top = GetHateTop(); + + // If no special flee_percent check for Gray or Other con rates + if(GetLevelCon(hate_top->GetLevel(), GetLevel()) == CON_GRAY && fleeratio == 0 && RuleB(Combat, FleeGray)) { + fleeratio = RuleI(Combat, FleeGrayHPRatio); + } else if(fleeratio == 0) { + fleeratio = RuleI(Combat, FleeHPRatio ); + } + + // Mob is still too low. Keep Running + if(hpratio < fleeratio) { return; + } //we are not dying anymore... see what we do next diff --git a/zone/merc.cpp b/zone/merc.cpp index 834b96e44..a4b331fff 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1414,7 +1414,7 @@ void Merc::AI_Process() { if(DivineAura()) return; - int hateCount = entity_list.GetHatedCount(this, nullptr); + int hateCount = entity_list.GetHatedCount(this, nullptr, false); if(GetHatedCount() < hateCount) { SetHatedCount(hateCount);