//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 && curfp) return; //dont bother if we are immune to fleeing if(SpecAttacks[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(); if(ratio >= RuleI(Combat, FleeHPRatio)) return; //we might be hurt enough, check con now.. Mob *hate_top = GetHateTop(); if(!hate_top) { //this should never happen... StartFleeing(); return; } float other_ratio = hate_top->GetHPRatio(); 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 uint32 con = GetLevelCon(hate_top->GetLevel(), GetLevel()); float run_ratio; switch(con) { //these values are not 100% researched case CON_GREEN: run_ratio = RuleI(Combat, FleeHPRatio); break; case CON_LIGHTBLUE: run_ratio = RuleI(Combat, FleeHPRatio) * 8 / 10; break; case CON_BLUE: run_ratio = RuleI(Combat, FleeHPRatio) * 6 / 10; break; default: run_ratio = RuleI(Combat, FleeHPRatio) * 4 / 10; break; } if(ratio < run_ratio) { if( RuleB(Combat, FleeIfNotAlone) || ( !RuleB(Combat, FleeIfNotAlone) && (entity_list.GetHatedCount(hate_top, this) == 0))) StartFleeing(); } }
/* If you change this function, you should update the above function to keep the #aggro command accurate. */ bool Mob::CheckWillAggro(Mob *mob) { if(!mob) return false; //sometimes if a client has some lag while zoning into a dangerous place while either invis or a GM //they will aggro mobs even though it's supposed to be impossible, to lets make sure we've finished connecting if (mob->IsClient()) { if (!mob->CastToClient()->ClientFinishedLoading() || mob->CastToClient()->IsHoveringForRespawn()) return false; } Mob *ownr = mob->GetOwner(); if(ownr && ownr->IsClient() && !ownr->CastToClient()->ClientFinishedLoading()) return false; float iAggroRange = GetAggroRange(); // Check If it's invisible and if we can see invis // Check if it's a client, and that the client is connected and not linkdead, // and that the client isn't Playing an NPC, with thier gm flag on // Check if it's not a Interactive NPC // Trumpcard: The 1st 3 checks are low cost calcs to filter out unnessecary distance checks. Leave them at the beginning, they are the most likely occurence. // Image: I moved this up by itself above faction and distance checks because if one of these return true, theres no reason to go through the other information float t1, t2, t3; t1 = mob->GetX() - GetX(); t2 = mob->GetY() - GetY(); t3 = mob->GetZ() - GetZ(); //Cheap ABS() if(t1 < 0) t1 = 0 - t1; if(t2 < 0) t2 = 0 - t2; if(t3 < 0) t3 = 0 - t3; if(( t1 > iAggroRange) || ( t2 > iAggroRange) || ( t3 > iAggroRange) ||(mob->IsInvisible(this)) || (mob->IsClient() && (!mob->CastToClient()->Connected() || mob->CastToClient()->IsLD() || mob->CastToClient()->IsBecomeNPC() || mob->CastToClient()->GetGM() ) )) { return(false); } // Don't aggro new clients if we are already engaged unless PROX_AGGRO is set if (IsEngaged() && (!GetSpecialAbility(PROX_AGGRO) || (GetSpecialAbility(PROX_AGGRO) && !CombatRange(mob)))) { Log.Out(Logs::Moderate, Logs::Aggro, "%s is in combat, and does not have prox_aggro, or does and is out of combat range with %s", GetName(), mob->GetName()); return false; } //im not sure I understand this.. //if I have an owner and it is not this mob, then I cannot //aggro this mob...??? //changed to be 'if I have an owner and this is it' if(mob == GetOwner()) { return(false); } float dist2 = DistanceSquared(mob->GetPosition(), m_Position); float iAggroRange2 = iAggroRange*iAggroRange; if( dist2 > iAggroRange2 ) { // Skip it, out of range return(false); } //Image: Get their current target and faction value now that its required //this function call should seem backwards FACTION_VALUE fv = mob->GetReverseFactionCon(this); // Make sure they're still in the zone // Are they in range? // Are they kos? // Are we stupid or are they green // and they don't have their gm flag on int heroicCHA_mod = mob->itembonuses.HeroicCHA/25; // 800 Heroic CHA cap if(heroicCHA_mod > THREATENLY_ARRGO_CHANCE) heroicCHA_mod = THREATENLY_ARRGO_CHANCE; if ( //old InZone check taken care of above by !mob->CastToClient()->Connected() ( ( GetINT() <= RuleI(Aggro, IntAggroThreshold) ) ||( mob->IsClient() && mob->CastToClient()->IsSitting() ) ||( mob->GetLevelCon(GetLevel()) != CON_GREEN ) ) && ( ( fv == FACTION_SCOWLS || (mob->GetPrimaryFaction() != GetPrimaryFaction() && mob->GetPrimaryFaction() == -4 && GetOwner() == nullptr) || ( fv == FACTION_THREATENLY && zone->random.Roll(THREATENLY_ARRGO_CHANCE - heroicCHA_mod) ) ) ) ) { //FatherNiwtit: make sure we can see them. last since it is very expensive if(CheckLosFN(mob)) { Log.Out(Logs::Detail, Logs::Aggro, "Check aggro for %s target %s.", GetName(), mob->GetName()); return( mod_will_aggro(mob, this) ); } } Log.Out(Logs::Detail, Logs::Aggro, "Is In zone?:%d\n", mob->InZone()); Log.Out(Logs::Detail, Logs::Aggro, "Dist^2: %f\n", dist2); Log.Out(Logs::Detail, Logs::Aggro, "Range^2: %f\n", iAggroRange2); Log.Out(Logs::Detail, Logs::Aggro, "Faction: %d\n", fv); Log.Out(Logs::Detail, Logs::Aggro, "Int: %d\n", GetINT()); Log.Out(Logs::Detail, Logs::Aggro, "Con: %d\n", GetLevelCon(mob->GetLevel())); return(false); }