mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-16 22:58:34 +00:00
Haynar's movement fixes.
Changes Speed from float to int. EQ client deals with int step locs better than it does floats according to Haynar's testing. This also contains mob runspeed changes. I recommend you set runspeeds to start in the DB 1.25 for NPCs below 1.25 which will match player runspeeds almost equally. Existing DBs will need to be updated. General Cleanup of MobAI functions. Mobs now change their heading on AIMovement timers if their targets' heading has changed since that time. This prevents players from being able to land backstabs inbetween mob swings. Charmed/feared players now send the appropriate packet, there was a missing CastToClient() in spells that was missing. Mob runspeed can no longer be snared to 0%, instead, 1% of their base runspeed is the maximum. Roots apply as roots instead of a modifier under this code. There is going to be bugs with this code. It's better we push through it than revert it. Sanctuary has been running this for a good week and we've worked through the issues. Misc updates: Exported some variables to perl, including: EVENT_ITE_CLICK_CAST: EVENT_ITEM_CLICK: spell_id - returns the spell_id of the click effect. return value - cancels the cast. EVENT_DROP_ITEM: quantity - returns the # of items dropped in the packet. If the item has charges, charges are returned here instead. itemname - name of the item being dropped itemid - id of the item being droppped spell_id - spell_id associated with the item's click effect. slotid - the inventory slot id of the item being dropped. return value - cancels the item from being dropped. Added Perl function: CalcEXP. Calculates the experience you would gain for an NPC that cons a specific con value to you. Fixed a bug where you would receive the group experience bonus and group experience messages for simply being in a group, regardless of the player being in the same zone as you.
This commit is contained in:
+137
-154
@@ -339,9 +339,7 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
|
||||
|
||||
//stop moving if were casting a spell and were not a bard...
|
||||
if(!IsBardSong(AIspells[i].spellid)) {
|
||||
SetRunAnimSpeed(0);
|
||||
SendPosition();
|
||||
SetMoving(false);
|
||||
SetCurrentSpeed(0);
|
||||
}
|
||||
|
||||
return CastSpell(AIspells[i].spellid, tar->GetID(), 1, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0, &(AIspells[i].resist_adjust));
|
||||
@@ -698,9 +696,7 @@ void Client::AI_SpellCast()
|
||||
{
|
||||
if(!IsBardSong(spell_to_cast))
|
||||
{
|
||||
SetRunAnimSpeed(0);
|
||||
SendPosition();
|
||||
SetMoving(false);
|
||||
SetCurrentSpeed(0);
|
||||
}
|
||||
CastSpell(spell_to_cast, tar->GetID(), slot_to_use);
|
||||
return;
|
||||
@@ -714,9 +710,7 @@ void Client::AI_SpellCast()
|
||||
{
|
||||
if(!IsBardSong(spell_to_cast))
|
||||
{
|
||||
SetRunAnimSpeed(0);
|
||||
SendPosition();
|
||||
SetMoving(false);
|
||||
SetCurrentSpeed(0);
|
||||
}
|
||||
CastSpell(spell_to_cast, tar->GetID(), slot_to_use);
|
||||
return;
|
||||
@@ -772,16 +766,13 @@ void Client::AI_Process()
|
||||
{
|
||||
if(GetTarget())
|
||||
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
|
||||
SetRunAnimSpeed(0);
|
||||
SendPosition();
|
||||
SetMoving(false);
|
||||
moved=false;
|
||||
SetCurrentSpeed(0);
|
||||
}
|
||||
//continue on to attack code, ensuring that we execute the engaged code
|
||||
engaged = true;
|
||||
} else {
|
||||
if(AImovement_timer->Check()) {
|
||||
animation = GetRunspeed() * 21;
|
||||
//animation = GetFearSpeed() * 21;
|
||||
// Check if we have reached the last fear point
|
||||
if ((std::abs(GetX() - m_FearWalkTarget.x) < 0.1) &&
|
||||
(std::abs(GetY() - m_FearWalkTarget.y) < 0.1)) {
|
||||
@@ -839,16 +830,13 @@ void Client::AI_Process()
|
||||
}
|
||||
|
||||
if (AImovement_timer->Check()) {
|
||||
SetRunAnimSpeed(0);
|
||||
if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w)
|
||||
{
|
||||
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
|
||||
SendPosition();
|
||||
}
|
||||
SetCurrentSpeed(0);
|
||||
}
|
||||
if(IsMoving()) {
|
||||
SetMoving(false);
|
||||
moved=false;
|
||||
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
|
||||
SendPosition();
|
||||
tar_ndx =0;
|
||||
}
|
||||
|
||||
if(GetTarget() && !IsStunned() && !IsMezzed() && !GetFeigned()) {
|
||||
if(attack_timer.Check()) {
|
||||
Attack(GetTarget(), MainPrimary);
|
||||
@@ -944,28 +932,27 @@ void Client::AI_Process()
|
||||
{
|
||||
if(!IsRooted())
|
||||
{
|
||||
animation = 21 * GetRunspeed();
|
||||
if(!RuleB(Pathing, Aggro) || !zone->pathing)
|
||||
CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed());
|
||||
else
|
||||
if(AImovement_timer->Check())
|
||||
{
|
||||
bool WaypointChanged, NodeReached;
|
||||
glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(),
|
||||
GetRunspeed(), WaypointChanged, NodeReached);
|
||||
if(!RuleB(Pathing, Aggro) || !zone->pathing)
|
||||
CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed());
|
||||
else
|
||||
{
|
||||
bool WaypointChanged, NodeReached;
|
||||
glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(),
|
||||
GetRunspeed(), WaypointChanged, NodeReached);
|
||||
|
||||
if(WaypointChanged)
|
||||
tar_ndx = 20;
|
||||
if(WaypointChanged)
|
||||
tar_ndx = 20;
|
||||
|
||||
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed());
|
||||
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(IsMoving())
|
||||
{
|
||||
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
|
||||
SetRunAnimSpeed(0);
|
||||
SendPosition();
|
||||
SetMoving(false);
|
||||
moved=false;
|
||||
SetCurrentSpeed(0);
|
||||
}
|
||||
}
|
||||
AI_SpellCast();
|
||||
@@ -998,21 +985,23 @@ void Client::AI_Process()
|
||||
return;
|
||||
|
||||
float dist = DistanceSquared(m_Position, owner->GetPosition());
|
||||
if (dist >= 100)
|
||||
if (dist >= 400)
|
||||
{
|
||||
float speed = dist >= 225 ? GetRunspeed() : GetWalkspeed();
|
||||
animation = 21 * speed;
|
||||
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
|
||||
if(AImovement_timer->Check())
|
||||
{
|
||||
int speed = GetWalkspeed();
|
||||
if (dist >= 5625)
|
||||
speed = GetRunspeed();
|
||||
|
||||
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetHeading(owner->GetHeading());
|
||||
if(moved)
|
||||
{
|
||||
moved=false;
|
||||
SetMoving(false);
|
||||
SendPosition();
|
||||
SetRunAnimSpeed(0);
|
||||
SetCurrentSpeed(0);
|
||||
moved = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1042,9 +1031,7 @@ void Mob::AI_Process() {
|
||||
{
|
||||
if(target)
|
||||
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY()));
|
||||
SetRunAnimSpeed(0);
|
||||
SendPosition();
|
||||
SetMoving(false);
|
||||
SetCurrentSpeed(0);
|
||||
moved=false;
|
||||
}
|
||||
//continue on to attack code, ensuring that we execute the engaged code
|
||||
@@ -1058,7 +1045,9 @@ void Mob::AI_Process() {
|
||||
CalculateNewFearpoint();
|
||||
}
|
||||
if(!RuleB(Pathing, Fear) || !zone->pathing)
|
||||
{
|
||||
CalculateNewPosition2(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, GetFearSpeed(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool WaypointChanged, NodeReached;
|
||||
@@ -1156,15 +1145,21 @@ void Mob::AI_Process() {
|
||||
{
|
||||
if (AImovement_timer->Check())
|
||||
{
|
||||
SetRunAnimSpeed(0);
|
||||
if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w)
|
||||
{
|
||||
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
|
||||
SendPosition();
|
||||
}
|
||||
SetCurrentSpeed(0);
|
||||
}
|
||||
if(IsMoving())
|
||||
{
|
||||
SetMoving(false);
|
||||
moved=false;
|
||||
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY()));
|
||||
SendPosition();
|
||||
tar_ndx =0;
|
||||
if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w)
|
||||
{
|
||||
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
|
||||
SendPosition();
|
||||
}
|
||||
SetCurrentSpeed(0);
|
||||
}
|
||||
|
||||
//casting checked above...
|
||||
@@ -1369,7 +1364,7 @@ void Mob::AI_Process() {
|
||||
CastToNPC()->DoClassAttacks(target);
|
||||
}
|
||||
AI_EngagedCastCheck();
|
||||
} //end is within combat range
|
||||
} //end is within combat rangepet
|
||||
else {
|
||||
//we cannot reach our target...
|
||||
//underwater stuff only works with water maps in the zone!
|
||||
@@ -1425,10 +1420,7 @@ void Mob::AI_Process() {
|
||||
}
|
||||
else if(IsMoving()) {
|
||||
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY()));
|
||||
SetRunAnimSpeed(0);
|
||||
SendPosition();
|
||||
SetMoving(false);
|
||||
moved=false;
|
||||
SetCurrentSpeed(0);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1481,7 +1473,6 @@ void Mob::AI_Process() {
|
||||
}
|
||||
else if (AImovement_timer->Check() && !IsRooted())
|
||||
{
|
||||
SetRunAnimSpeed(0);
|
||||
if (IsPet())
|
||||
{
|
||||
// we're a pet, do as we're told
|
||||
@@ -1500,18 +1491,18 @@ void Mob::AI_Process() {
|
||||
float dist = DistanceSquared(m_Position, owner->GetPosition());
|
||||
if (dist >= 400)
|
||||
{
|
||||
float speed = GetWalkspeed();
|
||||
int speed = GetWalkspeed();
|
||||
if (dist >= 5625)
|
||||
speed = GetRunspeed();
|
||||
|
||||
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(moved)
|
||||
{
|
||||
moved=false;
|
||||
SetMoving(false);
|
||||
SendPosition();
|
||||
SetCurrentSpeed(0);
|
||||
moved = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1557,19 +1548,15 @@ void Mob::AI_Process() {
|
||||
|
||||
if (dist2 >= followdist) // Default follow distance is 100
|
||||
{
|
||||
float speed = GetWalkspeed();
|
||||
int speed = GetWalkspeed();
|
||||
if (dist2 >= followdist + 150)
|
||||
speed = GetRunspeed();
|
||||
CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(moved)
|
||||
{
|
||||
SendPosition();
|
||||
moved=false;
|
||||
SetMoving(false);
|
||||
}
|
||||
moved = false;
|
||||
SetCurrentSpeed(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1666,40 +1653,92 @@ void NPC::AI_DoMovement() {
|
||||
|
||||
if (gridno > 0 || cur_wp==-2) {
|
||||
if (movetimercompleted==true) { // time to pause at wp is over
|
||||
AI_SetupNextWaypoint();
|
||||
|
||||
int32 spawn_id = this->GetSpawnPointID();
|
||||
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
|
||||
iterator.Reset();
|
||||
Spawn2 *found_spawn = nullptr;
|
||||
|
||||
while(iterator.MoreElements())
|
||||
{
|
||||
Spawn2* cur = iterator.GetData();
|
||||
iterator.Advance();
|
||||
if(cur->GetID() == spawn_id)
|
||||
{
|
||||
found_spawn = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) {
|
||||
CastToNPC()->Depop(true); //depop and resart spawn timer
|
||||
if(found_spawn)
|
||||
found_spawn->SetNPCPointerNull();
|
||||
}
|
||||
else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) {
|
||||
CastToNPC()->Depop(false);//depop without spawn timer
|
||||
if(found_spawn)
|
||||
found_spawn->SetNPCPointerNull();
|
||||
}
|
||||
else {
|
||||
movetimercompleted=false;
|
||||
|
||||
Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp);
|
||||
|
||||
//if we were under quest control (with no grid), we are done now..
|
||||
if(cur_wp == -2) {
|
||||
Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode.");
|
||||
roamer = false;
|
||||
cur_wp = 0;
|
||||
}
|
||||
|
||||
if(GetAppearance() != eaStanding)
|
||||
SetAppearance(eaStanding, false);
|
||||
|
||||
entity_list.OpenDoorsNear(CastToNPC());
|
||||
|
||||
if(!DistractedFromGrid) {
|
||||
//kick off event_waypoint depart
|
||||
char temp[16];
|
||||
sprintf(temp, "%d", cur_wp);
|
||||
parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0);
|
||||
|
||||
//setup our next waypoint, if we are still on our normal grid
|
||||
//remember that the quest event above could have done anything it wanted with our grid
|
||||
if(gridno > 0) {
|
||||
CastToNPC()->CalculateNewWaypoint();
|
||||
}
|
||||
}
|
||||
else {
|
||||
DistractedFromGrid = false;
|
||||
}
|
||||
}
|
||||
} // endif (movetimercompleted==true)
|
||||
else if (!(AIwalking_timer->Enabled()))
|
||||
{ // currently moving
|
||||
bool doMove = true;
|
||||
if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY())
|
||||
{ // are we there yet? then stop
|
||||
Log.Out(Logs::Detail, Logs::AI, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid());
|
||||
if (cur_wp_pause != 0) {
|
||||
SetWaypointPause();
|
||||
SetWaypointPause();
|
||||
if(GetAppearance() != eaStanding)
|
||||
SetAppearance(eaStanding, false);
|
||||
SetMoving(false);
|
||||
if (m_CurrentWayPoint.w >= 0.0) {
|
||||
SetHeading(m_CurrentWayPoint.w);
|
||||
}
|
||||
SendPosition();
|
||||
SetMoving(false);
|
||||
if (m_CurrentWayPoint.w >= 0.0) {
|
||||
SetHeading(m_CurrentWayPoint.w);
|
||||
}
|
||||
SendPosition();
|
||||
|
||||
//kick off event_waypoint arrive
|
||||
char temp[16];
|
||||
sprintf(temp, "%d", cur_wp);
|
||||
parse->EventNPC(EVENT_WAYPOINT_ARRIVE, CastToNPC(), nullptr, temp, 0);
|
||||
// start moving directly to next waypoint if we're at a 0 pause waypoint and we didn't get quest halted.
|
||||
if (!AIwalking_timer->Enabled())
|
||||
AI_SetupNextWaypoint();
|
||||
else
|
||||
doMove = false;
|
||||
|
||||
// wipe feign memory since we reached our first waypoint
|
||||
if(cur_wp == 1)
|
||||
ClearFeignMemory();
|
||||
}
|
||||
if (doMove)
|
||||
{ // not at waypoint yet or at 0 pause WP, so keep moving
|
||||
else
|
||||
{ // not at waypoint yet, so keep moving
|
||||
if(!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0))
|
||||
CalculateNewPosition2(m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, walksp, true);
|
||||
else
|
||||
@@ -1727,7 +1766,8 @@ void NPC::AI_DoMovement() {
|
||||
SetGrid( 0 - GetGrid()); // revert to AI control
|
||||
Log.Out(Logs::Detail, Logs::Pathing, "Quest pathing is finished. Resuming on grid %d", GetGrid());
|
||||
|
||||
SetAppearance(eaStanding, false);
|
||||
if(GetAppearance() != eaStanding)
|
||||
SetAppearance(eaStanding, false);
|
||||
|
||||
CalculateNewWaypoint();
|
||||
}
|
||||
@@ -1763,86 +1803,28 @@ void NPC::AI_DoMovement() {
|
||||
Log.Out(Logs::Detail, Logs::AI, "Reached guard point (%.3f,%.3f,%.3f)", m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z);
|
||||
ClearFeignMemory();
|
||||
moved=false;
|
||||
SetMoving(false);
|
||||
if (GetTarget() == nullptr || DistanceSquared(m_Position, GetTarget()->GetPosition()) >= 5*5 )
|
||||
{
|
||||
SetHeading(m_GuardPoint.w);
|
||||
} else {
|
||||
FaceTarget(GetTarget());
|
||||
}
|
||||
SendPosition();
|
||||
SetCurrentSpeed(0);
|
||||
SetAppearance(GetGuardPointAnim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void NPC::AI_SetupNextWaypoint() {
|
||||
int32 spawn_id = this->GetSpawnPointID();
|
||||
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
|
||||
iterator.Reset();
|
||||
Spawn2 *found_spawn = nullptr;
|
||||
|
||||
while (iterator.MoreElements())
|
||||
{
|
||||
Spawn2* cur = iterator.GetData();
|
||||
iterator.Advance();
|
||||
if (cur->GetID() == spawn_id)
|
||||
{
|
||||
found_spawn = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) {
|
||||
CastToNPC()->Depop(true); //depop and restart spawn timer
|
||||
if (found_spawn)
|
||||
found_spawn->SetNPCPointerNull();
|
||||
}
|
||||
else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) {
|
||||
CastToNPC()->Depop(false);//depop without spawn timer
|
||||
if (found_spawn)
|
||||
found_spawn->SetNPCPointerNull();
|
||||
}
|
||||
else {
|
||||
movetimercompleted = false;
|
||||
|
||||
Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp);
|
||||
|
||||
//if we were under quest control (with no grid), we are done now..
|
||||
if (cur_wp == -2) {
|
||||
Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode.");
|
||||
roamer = false;
|
||||
cur_wp = 0;
|
||||
}
|
||||
|
||||
SetAppearance(eaStanding, false);
|
||||
|
||||
entity_list.OpenDoorsNear(CastToNPC());
|
||||
|
||||
if (!DistractedFromGrid) {
|
||||
//kick off event_waypoint depart
|
||||
char temp[16];
|
||||
sprintf(temp, "%d", cur_wp);
|
||||
parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0);
|
||||
|
||||
//setup our next waypoint, if we are still on our normal grid
|
||||
//remember that the quest event above could have done anything it wanted with our grid
|
||||
if (GetGrid() > 0) {
|
||||
CastToNPC()->CalculateNewWaypoint();
|
||||
}
|
||||
}
|
||||
else {
|
||||
DistractedFromGrid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Mob that caused this may not get added to the hate list until after this function call completes
|
||||
void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) {
|
||||
if (!IsAIControlled())
|
||||
return;
|
||||
|
||||
SetAppearance(eaStanding);
|
||||
if(GetAppearance() != eaStanding)
|
||||
{
|
||||
SetAppearance(eaStanding);
|
||||
}
|
||||
|
||||
if (iYellForHelp) {
|
||||
if(IsPet()) {
|
||||
@@ -1889,9 +1871,10 @@ void Mob::AI_Event_NoLongerEngaged() {
|
||||
pLastFightingDelayMoving += zone->random.Int(minLastFightingDelayMoving, maxLastFightingDelayMoving);
|
||||
// So mobs don't keep running as a ghost until AIwalking_timer fires
|
||||
// if they were moving prior to losing all hate
|
||||
if(IsMoving()){
|
||||
// except if we're a pet, then we might run into some issues with pets backing off when they should immediately be moving
|
||||
if(!IsPet())
|
||||
{
|
||||
SetRunAnimSpeed(0);
|
||||
SetMoving(false);
|
||||
SendPosition();
|
||||
}
|
||||
ClearRampage();
|
||||
|
||||
Reference in New Issue
Block a user