mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-16 22:58:34 +00:00
Merge remote-tracking branch 'remotes/origin/master' into logging_changes
Conflicts: world/client.cpp world/worlddb.cpp zone/aggro.cpp zone/bot.cpp zone/client.cpp zone/client_packet.cpp zone/client_process.cpp zone/doors.cpp zone/entity.cpp zone/inventory.cpp zone/mob_ai.cpp zone/perl_client.cpp zone/spells.cpp zone/waypoints.cpp zone/zone.cpp zone/zonedb.cpp zone/zoning.cpp
This commit is contained in:
+158
-187
@@ -120,7 +120,7 @@ void NPC::ResumeWandering()
|
||||
return;
|
||||
}
|
||||
|
||||
if (cur_wp_x == GetX() && cur_wp_y == GetY())
|
||||
if (m_CurrentWayPoint.m_X == GetX() && m_CurrentWayPoint.m_Y == GetY())
|
||||
{ // are we we at a waypoint? if so, trigger event and start to next
|
||||
char temp[100];
|
||||
itoa(cur_wp,temp,10); //do this before updating to next waypoint
|
||||
@@ -159,7 +159,7 @@ void NPC::PauseWandering(int pausetime)
|
||||
return;
|
||||
}
|
||||
|
||||
void NPC::MoveTo(float mtx, float mty, float mtz, float mth, bool saveguardspot)
|
||||
void NPC::MoveTo(const xyz_heading& position, bool saveguardspot)
|
||||
{ // makes mob walk to specified location
|
||||
if (IsNPC() && GetGrid() != 0)
|
||||
{ // he is on a grid
|
||||
@@ -174,36 +174,30 @@ void NPC::MoveTo(float mtx, float mty, float mtz, float mth, bool saveguardspot)
|
||||
save_wp=cur_wp; // save the current waypoint
|
||||
cur_wp=-1; // flag this move as quest controlled
|
||||
}
|
||||
Log.Out(Logs::Detail, Logs::AI, "MoveTo (%.3f, %.3f, %.3f), pausing regular grid wandering. Grid %d, save_wp %d", mtx, mty, mtz, -GetGrid(), save_wp);
|
||||
Log.Out(Logs::Detail, Logs::AI, "MoveTo %s, pausing regular grid wandering. Grid %d, save_wp %d",to_string(static_cast<xyz_location>(position)).c_str(), -GetGrid(), save_wp);
|
||||
}
|
||||
else
|
||||
{ // not on a grid
|
||||
roamer=true;
|
||||
save_wp=0;
|
||||
cur_wp=-2; // flag as quest controlled w/no grid
|
||||
Log.Out(Logs::Detail, Logs::AI, "MoveTo (%.3f, %.3f, %.3f) without a grid.", mtx, mty, mtz);
|
||||
Log.Out(Logs::Detail, Logs::AI, "MoveTo %s without a grid.", to_string(static_cast<xyz_location>(position)).c_str());
|
||||
}
|
||||
if (saveguardspot)
|
||||
{
|
||||
guard_x = mtx;
|
||||
guard_y = mty;
|
||||
guard_z = mtz;
|
||||
guard_heading = mth;
|
||||
m_GuardPoint = position;
|
||||
|
||||
if(guard_heading == 0)
|
||||
guard_heading = 0.0001; //hack to make IsGuarding simpler
|
||||
if(m_GuardPoint.m_Heading == 0)
|
||||
m_GuardPoint.m_Heading = 0.0001; //hack to make IsGuarding simpler
|
||||
|
||||
if(guard_heading == -1)
|
||||
guard_heading = this->CalculateHeadingToTarget(mtx, mty);
|
||||
if(m_GuardPoint.m_Heading == -1)
|
||||
m_GuardPoint.m_Heading = this->CalculateHeadingToTarget(position.m_X, position.m_Y);
|
||||
|
||||
Log.Out(Logs::Detail, Logs::AI, "Setting guard position to (%.3f, %.3f, %.3f)", guard_x, guard_y, guard_z);
|
||||
Log.Out(Logs::Detail, Logs::AI, "Setting guard position to %s", to_string(static_cast<xyz_location>(m_GuardPoint)).c_str());
|
||||
}
|
||||
|
||||
cur_wp_x = mtx;
|
||||
cur_wp_y = mty;
|
||||
cur_wp_z = mtz;
|
||||
m_CurrentWayPoint = position;
|
||||
cur_wp_pause = 0;
|
||||
cur_wp_heading = mth;
|
||||
pLastFightingDelayMoving = 0;
|
||||
if(AIwalking_timer->Enabled())
|
||||
AIwalking_timer->Start(100);
|
||||
@@ -219,26 +213,23 @@ void NPC::UpdateWaypoint(int wp_index)
|
||||
cur = Waypoints.begin();
|
||||
cur += wp_index;
|
||||
|
||||
cur_wp_x = cur->x;
|
||||
cur_wp_y = cur->y;
|
||||
cur_wp_z = cur->z;
|
||||
m_CurrentWayPoint = xyz_heading(cur->x, cur->y, cur->z, cur->heading);
|
||||
cur_wp_pause = cur->pause;
|
||||
cur_wp_heading = cur->heading;
|
||||
Log.Out(Logs::Detail, Logs::AI, "Next waypoint %d: (%.3f, %.3f, %.3f, %.3f)", wp_index, cur_wp_x, cur_wp_y, cur_wp_z, cur_wp_heading);
|
||||
Log.Out(Logs::Detail, Logs::AI, "Next waypoint %d: (%.3f, %.3f, %.3f, %.3f)", wp_index, m_CurrentWayPoint.m_X, m_CurrentWayPoint.m_Y, m_CurrentWayPoint.m_Z, m_CurrentWayPoint.m_Heading);
|
||||
|
||||
//fix up pathing Z
|
||||
if(zone->HasMap() && RuleB(Map, FixPathingZAtWaypoints))
|
||||
{
|
||||
|
||||
if(!RuleB(Watermap, CheckForWaterAtWaypoints) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(cur_wp_x, cur_wp_y, cur_wp_z)))
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(m_CurrentWayPoint)))
|
||||
{
|
||||
Map::Vertex dest(cur_wp_x, cur_wp_y, cur_wp_z);
|
||||
Map::Vertex dest(m_CurrentWayPoint.m_X, m_CurrentWayPoint.m_Y, m_CurrentWayPoint.m_Z);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
|
||||
if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaWaypoint))
|
||||
cur_wp_z = newz + 1;
|
||||
m_CurrentWayPoint.m_Z = newz + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +258,7 @@ void NPC::CalculateNewWaypoint()
|
||||
case 1: //10 closest
|
||||
{
|
||||
std::list<wplist> closest;
|
||||
GetClosestWaypoint(closest, 10, GetX(), GetY(), GetZ());
|
||||
GetClosestWaypoint(closest, 10, GetPosition());
|
||||
std::list<wplist>::iterator iter = closest.begin();
|
||||
if(closest.size() != 0)
|
||||
{
|
||||
@@ -323,7 +314,7 @@ void NPC::CalculateNewWaypoint()
|
||||
case 5: //pick random closest 5 and pick one that's in sight
|
||||
{
|
||||
std::list<wplist> closest;
|
||||
GetClosestWaypoint(closest, 5, GetX(), GetY(), GetZ());
|
||||
GetClosestWaypoint(closest, 5, GetPosition());
|
||||
|
||||
std::list<wplist>::iterator iter = closest.begin();
|
||||
while(iter != closest.end())
|
||||
@@ -364,7 +355,7 @@ bool wp_distance_pred(const wp_distance& left, const wp_distance& right)
|
||||
return left.dist < right.dist;
|
||||
}
|
||||
|
||||
void NPC::GetClosestWaypoint(std::list<wplist> &wp_list, int count, float m_x, float m_y, float m_z)
|
||||
void NPC::GetClosestWaypoint(std::list<wplist> &wp_list, int count, const xyz_location& location)
|
||||
{
|
||||
wp_list.clear();
|
||||
if(Waypoints.size() <= count)
|
||||
@@ -379,11 +370,11 @@ void NPC::GetClosestWaypoint(std::list<wplist> &wp_list, int count, float m_x, f
|
||||
std::list<wp_distance> distances;
|
||||
for(int i = 0; i < Waypoints.size(); ++i)
|
||||
{
|
||||
float cur_x = (Waypoints[i].x - m_x);
|
||||
float cur_x = (Waypoints[i].x - location.m_X);
|
||||
cur_x *= cur_x;
|
||||
float cur_y = (Waypoints[i].y - m_y);
|
||||
float cur_y = (Waypoints[i].y - location.m_Y);
|
||||
cur_y *= cur_y;
|
||||
float cur_z = (Waypoints[i].z - m_z);
|
||||
float cur_z = (Waypoints[i].z - location.m_Z);
|
||||
cur_z *= cur_z;
|
||||
float cur_dist = cur_x + cur_y + cur_z;
|
||||
wp_distance w_dist;
|
||||
@@ -431,28 +422,23 @@ void NPC::SetWaypointPause()
|
||||
void NPC::SaveGuardSpot(bool iClearGuardSpot) {
|
||||
if (iClearGuardSpot) {
|
||||
Log.Out(Logs::Detail, Logs::AI, "Clearing guard order.");
|
||||
guard_x = 0;
|
||||
guard_y = 0;
|
||||
guard_z = 0;
|
||||
guard_heading = 0;
|
||||
m_GuardPoint = xyz_heading(0, 0, 0, 0);
|
||||
}
|
||||
else {
|
||||
guard_x = x_pos;
|
||||
guard_y = y_pos;
|
||||
guard_z = z_pos;
|
||||
guard_heading = heading;
|
||||
if(guard_heading == 0)
|
||||
guard_heading = 0.0001; //hack to make IsGuarding simpler
|
||||
Log.Out(Logs::Detail, Logs::AI, "Setting guard position to (%.3f, %.3f, %.3f)", guard_x, guard_y, guard_z);
|
||||
m_GuardPoint = m_Position;
|
||||
|
||||
if(m_GuardPoint.m_Heading == 0)
|
||||
m_GuardPoint.m_Heading = 0.0001; //hack to make IsGuarding simpler
|
||||
Log.Out(Logs::Detail, Logs::AI, "Setting guard position to %s", to_string(static_cast<xyz_location>(m_GuardPoint)).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void NPC::NextGuardPosition() {
|
||||
if (!CalculateNewPosition2(guard_x, guard_y, guard_z, GetMovespeed())) {
|
||||
SetHeading(guard_heading);
|
||||
if (!CalculateNewPosition2(m_GuardPoint.m_X, m_GuardPoint.m_Y, m_GuardPoint.m_Z, GetMovespeed())) {
|
||||
SetHeading(m_GuardPoint.m_Heading);
|
||||
Log.Out(Logs::Detail, Logs::AI, "Unable to move to next guard position. Probably rooted.");
|
||||
}
|
||||
else if((x_pos == guard_x) && (y_pos == guard_y) && (z_pos == guard_z))
|
||||
else if((m_Position.m_X == m_GuardPoint.m_X) && (m_Position.m_Y == m_GuardPoint.m_Y) && (m_Position.m_Z == m_GuardPoint.m_Z))
|
||||
{
|
||||
if(moved)
|
||||
{
|
||||
@@ -480,7 +466,7 @@ void Mob::SaveSpawnSpot() {
|
||||
}*/
|
||||
|
||||
float Mob::CalculateDistance(float x, float y, float z) {
|
||||
return (float)sqrtf( ((x_pos-x)*(x_pos-x)) + ((y_pos-y)*(y_pos-y)) + ((z_pos-z)*(z_pos-z)) );
|
||||
return (float)sqrtf( ((m_Position.m_X-x)*(m_Position.m_X-x)) + ((m_Position.m_Y-y)*(m_Position.m_Y-y)) + ((m_Position.m_Z-z)*(m_Position.m_Z-z)) );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -491,13 +477,13 @@ uint8 NPC::CalculateHeadingToNextWaypoint() {
|
||||
float Mob::CalculateHeadingToTarget(float in_x, float in_y) {
|
||||
float angle;
|
||||
|
||||
if (in_x-x_pos > 0)
|
||||
angle = - 90 + atan((float)(in_y-y_pos) / (float)(in_x-x_pos)) * 180 / M_PI;
|
||||
else if (in_x-x_pos < 0)
|
||||
angle = + 90 + atan((float)(in_y-y_pos) / (float)(in_x-x_pos)) * 180 / M_PI;
|
||||
if (in_x-m_Position.m_X > 0)
|
||||
angle = - 90 + atan((float)(in_y-m_Position.m_Y) / (float)(in_x-m_Position.m_X)) * 180 / M_PI;
|
||||
else if (in_x-m_Position.m_X < 0)
|
||||
angle = + 90 + atan((float)(in_y-m_Position.m_Y) / (float)(in_x-m_Position.m_X)) * 180 / M_PI;
|
||||
else // Added?
|
||||
{
|
||||
if (in_y-y_pos > 0)
|
||||
if (in_y-m_Position.m_Y > 0)
|
||||
angle = 0;
|
||||
else
|
||||
angle = 180;
|
||||
@@ -513,16 +499,16 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
if(GetID()==0)
|
||||
return true;
|
||||
|
||||
if ((x_pos-x == 0) && (y_pos-y == 0)) {//spawn is at target coords
|
||||
if(z_pos-z != 0) {
|
||||
z_pos = z;
|
||||
if ((m_Position.m_X-x == 0) && (m_Position.m_Y-y == 0)) {//spawn is at target coords
|
||||
if(m_Position.m_Z-z != 0) {
|
||||
m_Position.m_Z = z;
|
||||
Log.Out(Logs::Detail, Logs::AI, "Calc Position2 (%.3f, %.3f, %.3f): Jumping pure Z.", x, y, z);
|
||||
return true;
|
||||
}
|
||||
Log.Out(Logs::Detail, Logs::AI, "Calc Position2 (%.3f, %.3f, %.3f) inWater=%d: We are there.", x, y, z, inWater);
|
||||
return false;
|
||||
}
|
||||
else if ((ABS(x_pos - x) < 0.1) && (ABS(y_pos - y) < 0.1))
|
||||
else if ((ABS(m_Position.m_X - x) < 0.1) && (ABS(m_Position.m_Y - y) < 0.1))
|
||||
{
|
||||
Log.Out(Logs::Detail, Logs::AI, "Calc Position2 (%.3f, %.3f, %.3f): X/Y difference <0.1, Jumping to target.", x, y, z);
|
||||
|
||||
@@ -530,27 +516,27 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
entity_list.ProcessMove(CastToNPC(), x, y, z);
|
||||
}
|
||||
|
||||
x_pos = x;
|
||||
y_pos = y;
|
||||
z_pos = z;
|
||||
m_Position.m_X = x;
|
||||
m_Position.m_Y = y;
|
||||
m_Position.m_Z = z;
|
||||
return true;
|
||||
}
|
||||
|
||||
int compare_steps = IsBoat() ? 1 : 20;
|
||||
if(tar_ndx < compare_steps && tarx==x && tary==y) {
|
||||
if(tar_ndx < compare_steps && m_TargetLocation.m_X==x && m_TargetLocation.m_Y==y) {
|
||||
|
||||
float new_x = x_pos + tar_vx*tar_vector;
|
||||
float new_y = y_pos + tar_vy*tar_vector;
|
||||
float new_z = z_pos + tar_vz*tar_vector;
|
||||
float new_x = m_Position.m_X + m_TargetV.m_X*tar_vector;
|
||||
float new_y = m_Position.m_Y + m_TargetV.m_Y*tar_vector;
|
||||
float new_z = m_Position.m_Z + m_TargetV.m_Z*tar_vector;
|
||||
if(IsNPC()) {
|
||||
entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z);
|
||||
}
|
||||
|
||||
x_pos = new_x;
|
||||
y_pos = new_y;
|
||||
z_pos = new_z;
|
||||
m_Position.m_X = new_x;
|
||||
m_Position.m_Y = new_y;
|
||||
m_Position.m_Z = new_z;
|
||||
|
||||
Log.Out(Logs::Detail, Logs::AI, "Calculating new position2 to (%.3f, %.3f, %.3f), old vector (%.3f, %.3f, %.3f)", x, y, z, tar_vx, tar_vy, tar_vz);
|
||||
Log.Out(Logs::Detail, Logs::AI, "Calculating new position2 to (%.3f, %.3f, %.3f), old vector (%.3f, %.3f, %.3f)", x, y, z, m_TargetV.m_X, m_TargetV.m_Y, m_TargetV.m_Z);
|
||||
|
||||
uint8 NPCFlyMode = 0;
|
||||
|
||||
@@ -563,25 +549,25 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving))
|
||||
{
|
||||
if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos)))
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(m_Position.m_X)))
|
||||
{
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
Map::Vertex dest(m_Position.m_X, m_Position.m_Y, m_Position.m_Z);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
|
||||
|
||||
Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,m_Position.m_X,m_Position.m_Y,m_Position.m_Z);
|
||||
|
||||
if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check.
|
||||
{
|
||||
if((ABS(x - x_pos) < 0.5) && (ABS(y - y_pos) < 0.5))
|
||||
if((ABS(x - m_Position.m_X) < 0.5) && (ABS(y - m_Position.m_Y) < 0.5))
|
||||
{
|
||||
if(ABS(z-z_pos) <= RuleR(Map, FixPathingZMaxDeltaMoving))
|
||||
z_pos = z;
|
||||
if(ABS(z-m_Position.m_Z) <= RuleR(Map, FixPathingZMaxDeltaMoving))
|
||||
m_Position.m_Z = z;
|
||||
else
|
||||
z_pos = newz + 1;
|
||||
m_Position.m_Z = newz + 1;
|
||||
}
|
||||
else
|
||||
z_pos = newz + 1;
|
||||
m_Position.m_Z = newz + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -596,28 +582,26 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
} else {
|
||||
tar_ndx=0;
|
||||
}
|
||||
tarx=x;
|
||||
tary=y;
|
||||
tarz=z;
|
||||
m_TargetLocation = xyz_location(x, y, z);
|
||||
|
||||
float nx = this->x_pos;
|
||||
float ny = this->y_pos;
|
||||
float nz = this->z_pos;
|
||||
float nx = this->m_Position.m_X;
|
||||
float ny = this->m_Position.m_Y;
|
||||
float nz = this->m_Position.m_Z;
|
||||
// float nh = this->heading;
|
||||
|
||||
tar_vx = x - nx;
|
||||
tar_vy = y - ny;
|
||||
tar_vz = z - nz;
|
||||
m_TargetV.m_X = x - nx;
|
||||
m_TargetV.m_Y = y - ny;
|
||||
m_TargetV.m_Z = z - nz;
|
||||
|
||||
//pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO);
|
||||
//speed *= NPC_SPEED_MULTIPLIER;
|
||||
|
||||
Log.Out(Logs::Detail, Logs::AI, "Calculating new position2 to (%.3f, %.3f, %.3f), new vector (%.3f, %.3f, %.3f) rate %.3f, RAS %d", x, y, z, tar_vx, tar_vy, tar_vz, speed, pRunAnimSpeed);
|
||||
Log.Out(Logs::Detail, Logs::AI, "Calculating new position2 to (%.3f, %.3f, %.3f), new vector (%.3f, %.3f, %.3f) rate %.3f, RAS %d", x, y, z, m_TargetV.m_X, m_TargetV.m_Y, m_TargetV.m_Z, speed, pRunAnimSpeed);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// 2: get unit vector
|
||||
// --------------------------------------------------------------------------
|
||||
float mag = sqrtf (tar_vx*tar_vx + tar_vy*tar_vy + tar_vz*tar_vz);
|
||||
float mag = sqrtf (m_TargetV.m_X*m_TargetV.m_X + m_TargetV.m_Y*m_TargetV.m_Y + m_TargetV.m_Z*m_TargetV.m_Z);
|
||||
tar_vector = speed / mag;
|
||||
|
||||
// mob move fix
|
||||
@@ -630,24 +614,24 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
{
|
||||
if (numsteps>1)
|
||||
{
|
||||
tar_vector=1.0f ;
|
||||
tar_vx = tar_vx/numsteps;
|
||||
tar_vy = tar_vy/numsteps;
|
||||
tar_vz = tar_vz/numsteps;
|
||||
tar_vector=1.0f ;
|
||||
m_TargetV.m_X = m_TargetV.m_X/numsteps;
|
||||
m_TargetV.m_Y = m_TargetV.m_Y/numsteps;
|
||||
m_TargetV.m_Z = m_TargetV.m_Z/numsteps;
|
||||
|
||||
float new_x = x_pos + tar_vx;
|
||||
float new_y = y_pos + tar_vy;
|
||||
float new_z = z_pos + tar_vz;
|
||||
float new_x = m_Position.m_X + m_TargetV.m_X;
|
||||
float new_y = m_Position.m_Y + m_TargetV.m_Y;
|
||||
float new_z = m_Position.m_Z + m_TargetV.m_Z;
|
||||
if(IsNPC()) {
|
||||
entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z);
|
||||
}
|
||||
|
||||
x_pos = new_x;
|
||||
y_pos = new_y;
|
||||
z_pos = new_z;
|
||||
m_Position.m_X = new_x;
|
||||
m_Position.m_Y = new_y;
|
||||
m_Position.m_Z = new_z;
|
||||
m_Position.m_Heading = CalculateHeadingToTarget(x, y);
|
||||
tar_ndx=22-numsteps;
|
||||
heading = CalculateHeadingToTarget(x, y);
|
||||
Log.Out(Logs::Detail, Logs::AI, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", x_pos, y_pos, z_pos, numsteps);
|
||||
Log.Out(Logs::Detail, Logs::AI, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", m_Position.m_X, m_Position.m_Y, m_Position.m_Z, numsteps);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -655,9 +639,9 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
entity_list.ProcessMove(CastToNPC(), x, y, z);
|
||||
}
|
||||
|
||||
x_pos = x;
|
||||
y_pos = y;
|
||||
z_pos = z;
|
||||
m_Position.m_X = x;
|
||||
m_Position.m_Y = y;
|
||||
m_Position.m_Z = z;
|
||||
|
||||
Log.Out(Logs::Detail, Logs::AI, "Only a single step to get there... jumping.");
|
||||
|
||||
@@ -667,18 +651,18 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
else {
|
||||
tar_vector/=20;
|
||||
|
||||
float new_x = x_pos + tar_vx*tar_vector;
|
||||
float new_y = y_pos + tar_vy*tar_vector;
|
||||
float new_z = z_pos + tar_vz*tar_vector;
|
||||
float new_x = m_Position.m_X + m_TargetV.m_X*tar_vector;
|
||||
float new_y = m_Position.m_Y + m_TargetV.m_Y*tar_vector;
|
||||
float new_z = m_Position.m_Z + m_TargetV.m_Z*tar_vector;
|
||||
if(IsNPC()) {
|
||||
entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z);
|
||||
}
|
||||
|
||||
x_pos = new_x;
|
||||
y_pos = new_y;
|
||||
z_pos = new_z;
|
||||
heading = CalculateHeadingToTarget(x, y);
|
||||
Log.Out(Logs::Detail, Logs::AI, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", x_pos, y_pos, z_pos, numsteps);
|
||||
m_Position.m_X = new_x;
|
||||
m_Position.m_Y = new_y;
|
||||
m_Position.m_Z = new_z;
|
||||
m_Position.m_Heading = CalculateHeadingToTarget(x, y);
|
||||
Log.Out(Logs::Detail, Logs::AI, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", m_Position.m_X, m_Position.m_Y, m_Position.m_Z, numsteps);
|
||||
}
|
||||
|
||||
uint8 NPCFlyMode = 0;
|
||||
@@ -692,25 +676,25 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) {
|
||||
|
||||
if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos)))
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(m_Position)))
|
||||
{
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
Map::Vertex dest(m_Position.m_X, m_Position.m_Y, m_Position.m_Z);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
|
||||
Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,m_Position.m_X,m_Position.m_Y,m_Position.m_Z);
|
||||
|
||||
if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check.
|
||||
{
|
||||
if(ABS(x - x_pos) < 0.5 && ABS(y - y_pos) < 0.5)
|
||||
if(ABS(x - m_Position.m_X) < 0.5 && ABS(y - m_Position.m_Y) < 0.5)
|
||||
{
|
||||
if(ABS(z - z_pos) <= RuleR(Map, FixPathingZMaxDeltaMoving))
|
||||
z_pos = z;
|
||||
if(ABS(z - m_Position.m_Z) <= RuleR(Map, FixPathingZMaxDeltaMoving))
|
||||
m_Position.m_Z = z;
|
||||
else
|
||||
z_pos = newz + 1;
|
||||
m_Position.m_Z = newz + 1;
|
||||
}
|
||||
else
|
||||
z_pos = newz+1;
|
||||
m_Position.m_Z = newz+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -718,10 +702,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
SetMoving(true);
|
||||
moved=true;
|
||||
|
||||
delta_x=x_pos-nx;
|
||||
delta_y=y_pos-ny;
|
||||
delta_z=z_pos-nz;
|
||||
delta_heading=0;
|
||||
m_Delta = xyz_heading(m_Position.m_X - nx, m_Position.m_Y - ny, m_Position.m_Z - nz, 0.0f);
|
||||
|
||||
if (IsClient())
|
||||
SendPosUpdate(1);
|
||||
@@ -746,9 +727,9 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec
|
||||
if(GetID()==0)
|
||||
return true;
|
||||
|
||||
float nx = x_pos;
|
||||
float ny = y_pos;
|
||||
float nz = z_pos;
|
||||
float nx = m_Position.m_X;
|
||||
float ny = m_Position.m_Y;
|
||||
float nz = m_Position.m_Z;
|
||||
|
||||
// if NPC is rooted
|
||||
if (speed == 0.0) {
|
||||
@@ -764,46 +745,46 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec
|
||||
}
|
||||
|
||||
float old_test_vector=test_vector;
|
||||
tar_vx = x - nx;
|
||||
tar_vy = y - ny;
|
||||
tar_vz = z - nz;
|
||||
m_TargetV.m_X = x - nx;
|
||||
m_TargetV.m_Y = y - ny;
|
||||
m_TargetV.m_Z = z - nz;
|
||||
|
||||
if (tar_vx == 0 && tar_vy == 0)
|
||||
if (m_TargetV.m_X == 0 && m_TargetV.m_Y == 0)
|
||||
return false;
|
||||
pRunAnimSpeed = (uint8)(speed*NPC_RUNANIM_RATIO);
|
||||
speed *= NPC_SPEED_MULTIPLIER;
|
||||
|
||||
Log.Out(Logs::Detail, Logs::AI, "Calculating new position to (%.3f, %.3f, %.3f) vector (%.3f, %.3f, %.3f) rate %.3f RAS %d", x, y, z, tar_vx, tar_vy, tar_vz, speed, pRunAnimSpeed);
|
||||
Log.Out(Logs::Detail, Logs::AI, "Calculating new position to (%.3f, %.3f, %.3f) vector (%.3f, %.3f, %.3f) rate %.3f RAS %d", x, y, z, m_TargetV.m_X, m_TargetV.m_Y, m_TargetV.m_Z, speed, pRunAnimSpeed);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// 2: get unit vector
|
||||
// --------------------------------------------------------------------------
|
||||
test_vector=sqrtf (x*x + y*y + z*z);
|
||||
tar_vector = speed / sqrtf (tar_vx*tar_vx + tar_vy*tar_vy + tar_vz*tar_vz);
|
||||
heading = CalculateHeadingToTarget(x, y);
|
||||
tar_vector = speed / sqrtf (m_TargetV.m_X*m_TargetV.m_X + m_TargetV.m_Y*m_TargetV.m_Y + m_TargetV.m_Z*m_TargetV.m_Z);
|
||||
m_Position.m_Heading = CalculateHeadingToTarget(x, y);
|
||||
|
||||
if (tar_vector >= 1.0) {
|
||||
if(IsNPC()) {
|
||||
entity_list.ProcessMove(CastToNPC(), x, y, z);
|
||||
}
|
||||
|
||||
x_pos = x;
|
||||
y_pos = y;
|
||||
z_pos = z;
|
||||
m_Position.m_X = x;
|
||||
m_Position.m_Y = y;
|
||||
m_Position.m_Z = z;
|
||||
Log.Out(Logs::Detail, Logs::AI, "Close enough, jumping to waypoint");
|
||||
}
|
||||
else {
|
||||
float new_x = x_pos + tar_vx*tar_vector;
|
||||
float new_y = y_pos + tar_vy*tar_vector;
|
||||
float new_z = z_pos + tar_vz*tar_vector;
|
||||
float new_x = m_Position.m_X + m_TargetV.m_X*tar_vector;
|
||||
float new_y = m_Position.m_Y + m_TargetV.m_Y*tar_vector;
|
||||
float new_z = m_Position.m_Z + m_TargetV.m_Z*tar_vector;
|
||||
if(IsNPC()) {
|
||||
entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z);
|
||||
}
|
||||
|
||||
x_pos = new_x;
|
||||
y_pos = new_y;
|
||||
z_pos = new_z;
|
||||
Log.Out(Logs::Detail, Logs::AI, "Next position (%.3f, %.3f, %.3f)", x_pos, y_pos, z_pos);
|
||||
m_Position.m_X = new_x;
|
||||
m_Position.m_Y = new_y;
|
||||
m_Position.m_Z = new_z;
|
||||
Log.Out(Logs::Detail, Logs::AI, "Next position (%.3f, %.3f, %.3f)", m_Position.m_X, m_Position.m_Y, m_Position.m_Z);
|
||||
}
|
||||
|
||||
uint8 NPCFlyMode = 0;
|
||||
@@ -817,25 +798,25 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec
|
||||
if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving))
|
||||
{
|
||||
if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos)))
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(m_Position)))
|
||||
{
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
Map::Vertex dest(m_Position.m_X, m_Position.m_Y, m_Position.m_Z);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
|
||||
|
||||
Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,m_Position.m_X,m_Position.m_Y,m_Position.m_Z);
|
||||
|
||||
if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check.
|
||||
{
|
||||
if(ABS(x - x_pos) < 0.5 && ABS(y - y_pos) < 0.5)
|
||||
if(ABS(x - m_Position.m_X) < 0.5 && ABS(y - m_Position.m_Y) < 0.5)
|
||||
{
|
||||
if(ABS(z - z_pos) <= RuleR(Map, FixPathingZMaxDeltaMoving))
|
||||
z_pos = z;
|
||||
if(ABS(z - m_Position.m_Z) <= RuleR(Map, FixPathingZMaxDeltaMoving))
|
||||
m_Position.m_Z = z;
|
||||
else
|
||||
z_pos = newz + 1;
|
||||
m_Position.m_Z = newz + 1;
|
||||
}
|
||||
else
|
||||
z_pos = newz+1;
|
||||
m_Position.m_Z = newz+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -845,10 +826,7 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec
|
||||
tar_ndx=0;
|
||||
this->SetMoving(true);
|
||||
moved=true;
|
||||
delta_x=(x_pos-nx);
|
||||
delta_y=(y_pos-ny);
|
||||
delta_z=(z_pos-nz);
|
||||
delta_heading=0;//(heading-nh)*8;
|
||||
m_Delta = xyz_heading(m_Position.m_X - nx, m_Position.m_Y - ny, m_Position.m_Z - nz, 0.0f);
|
||||
SendPosUpdate();
|
||||
}
|
||||
tar_ndx++;
|
||||
@@ -914,8 +892,9 @@ void NPC::AssignWaypoints(int32 grid) {
|
||||
|
||||
if(zone->HasMap() && RuleB(Map, FixPathingZWhenLoading) )
|
||||
{
|
||||
auto positon = xyz_location(newwp.x,newwp.y,newwp.z);
|
||||
if(!RuleB(Watermap, CheckWaypointsInWaterWhenLoading) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(newwp.x, newwp.y, newwp.z)))
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(positon)))
|
||||
{
|
||||
Map::Vertex dest(newwp.x, newwp.y, newwp.z);
|
||||
|
||||
@@ -948,9 +927,9 @@ void Mob::SendTo(float new_x, float new_y, float new_z) {
|
||||
entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z);
|
||||
}
|
||||
|
||||
x_pos = new_x;
|
||||
y_pos = new_y;
|
||||
z_pos = new_z;
|
||||
m_Position.m_X = new_x;
|
||||
m_Position.m_Y = new_y;
|
||||
m_Position.m_Z = new_z;
|
||||
Log.Out(Logs::Detail, Logs::AI, "Sent To (%.3f, %.3f, %.3f)", new_x, new_y, new_z);
|
||||
|
||||
if(flymode == FlyMode1)
|
||||
@@ -961,20 +940,20 @@ void Mob::SendTo(float new_x, float new_y, float new_z) {
|
||||
if(zone->HasMap() && RuleB(Map, FixPathingZOnSendTo) )
|
||||
{
|
||||
if(!RuleB(Watermap, CheckForWaterOnSendTo) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos)))
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(m_Position)))
|
||||
{
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
Map::Vertex dest(m_Position.m_X, m_Position.m_Y, m_Position.m_Z);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
|
||||
Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,m_Position.m_X,m_Position.m_Y,m_Position.m_Z);
|
||||
|
||||
if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaSendTo)) // Sanity check.
|
||||
z_pos = newz + 1;
|
||||
m_Position.m_Z = newz + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
z_pos += 0.1;
|
||||
m_Position.m_Z += 0.1;
|
||||
}
|
||||
|
||||
void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
|
||||
@@ -982,9 +961,9 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
|
||||
entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z + 0.1);
|
||||
}
|
||||
|
||||
x_pos = new_x;
|
||||
y_pos = new_y;
|
||||
z_pos = new_z + 0.1;
|
||||
m_Position.m_X = new_x;
|
||||
m_Position.m_Y = new_y;
|
||||
m_Position.m_Z = new_z + 0.1;
|
||||
|
||||
//fix up pathing Z, this shouldent be needed IF our waypoints
|
||||
//are corrected instead
|
||||
@@ -992,16 +971,16 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
|
||||
if(zone->HasMap() && RuleB(Map, FixPathingZOnSendTo))
|
||||
{
|
||||
if(!RuleB(Watermap, CheckForWaterOnSendTo) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos)))
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(m_Position)))
|
||||
{
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
Map::Vertex dest(m_Position.m_X, m_Position.m_Y, m_Position.m_Z);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
|
||||
Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,m_Position.m_X,m_Position.m_Y,m_Position.m_Z);
|
||||
|
||||
if( (newz > -2000) && ABS(newz-dest.z) < RuleR(Map, FixPathingZMaxDeltaSendTo)) // Sanity check.
|
||||
z_pos = newz + 1;
|
||||
m_Position.m_Z = newz + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1064,15 +1043,14 @@ bool ZoneDatabase::GetWaypoints(uint32 grid, uint16 zoneid, uint32 num, wplist*
|
||||
return true;
|
||||
}
|
||||
|
||||
void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid)
|
||||
void ZoneDatabase::AssignGrid(Client *client, const xy_location& location, uint32 grid)
|
||||
{
|
||||
int matches = 0, fuzzy = 0, spawn2id = 0;
|
||||
float dbx = 0, dby = 0;
|
||||
|
||||
// looks like most of the stuff in spawn2 is straight integers
|
||||
// so let's try that first
|
||||
std::string query = StringFormat("SELECT id, x, y FROM spawn2 WHERE zone = '%s' AND x = %i AND y = %i",
|
||||
zone->GetShortName(), (int)x, (int)y);
|
||||
zone->GetShortName(), (int)location.m_X, (int)location.m_Y);
|
||||
auto results = QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
return;
|
||||
@@ -1085,7 +1063,7 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid)
|
||||
query = StringFormat("SELECT id,x,y FROM spawn2 WHERE zone='%s' AND "
|
||||
"ABS( ABS(x) - ABS(%f) ) < %f AND "
|
||||
"ABS( ABS(y) - ABS(%f) ) < %f",
|
||||
zone->GetShortName(), x, _GASSIGN_TOLERANCE, y, _GASSIGN_TOLERANCE);
|
||||
zone->GetShortName(), location.m_X, _GASSIGN_TOLERANCE, location.m_Y, _GASSIGN_TOLERANCE);
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return;
|
||||
@@ -1110,8 +1088,7 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid)
|
||||
auto row = results.begin();
|
||||
|
||||
spawn2id = atoi(row[0]);
|
||||
dbx = atof(row[1]);
|
||||
dby = atof(row[2]);
|
||||
xy_location dbLocation = xy_location(atof(row[1]), atof(row[2]));
|
||||
|
||||
query = StringFormat("UPDATE spawn2 SET pathgrid = %d WHERE id = %d", grid, spawn2id);
|
||||
results = QueryDatabase(query);
|
||||
@@ -1135,7 +1112,7 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid)
|
||||
return;
|
||||
}
|
||||
|
||||
float difference = sqrtf(pow(fabs(x - dbx) , 2) + pow(fabs(y - dby), 2));
|
||||
float difference = sqrtf(pow(fabs(location.m_X - dbLocation.m_X) , 2) + pow(fabs(location.m_Y - dbLocation.m_Y), 2));
|
||||
client->Message(0, "Grid assign: spawn2 id = %d updated - fuzzy match: deviation %f", spawn2id, difference);
|
||||
}
|
||||
|
||||
@@ -1174,11 +1151,11 @@ void ZoneDatabase::ModifyGrid(Client *client, bool remove, uint32 id, uint8 type
|
||||
/**************************************
|
||||
* AddWP - Adds a new waypoint to a specific grid for a specific zone.
|
||||
*/
|
||||
void ZoneDatabase::AddWP(Client *client, uint32 gridid, uint32 wpnum, float xpos, float ypos, float zpos, uint32 pause, uint16 zoneid, float heading)
|
||||
void ZoneDatabase::AddWP(Client *client, uint32 gridid, uint32 wpnum, const xyz_heading& position, uint32 pause, uint16 zoneid)
|
||||
{
|
||||
std::string query = StringFormat("INSERT INTO grid_entries (gridid, zoneid, `number`, x, y, z, pause, heading) "
|
||||
"VALUES (%i, %i, %i, %f, %f, %f, %i, %f)",
|
||||
gridid, zoneid, wpnum, xpos, ypos, zpos, pause, heading);
|
||||
gridid, zoneid, wpnum, position.m_X, position.m_Y, position.m_Z, pause, position.m_Heading);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return;
|
||||
@@ -1221,7 +1198,7 @@ void ZoneDatabase::DeleteWaypoint(Client *client, uint32 grid_num, uint32 wp_num
|
||||
* Returns 0 if the function didn't have to create a new grid. If the function had to create a new grid for the spawn, then the ID of
|
||||
* the created grid is returned.
|
||||
*/
|
||||
uint32 ZoneDatabase::AddWPForSpawn(Client *client, uint32 spawn2id, float xpos, float ypos, float zpos, uint32 pause, int type1, int type2, uint16 zoneid, float heading) {
|
||||
uint32 ZoneDatabase::AddWPForSpawn(Client *client, uint32 spawn2id, const xyz_heading& position, uint32 pause, int type1, int type2, uint16 zoneid) {
|
||||
|
||||
uint32 grid_num; // The grid number the spawn is assigned to (if spawn has no grid, will be the grid number we end up creating)
|
||||
uint32 next_wp_num; // The waypoint number we should be assigning to the new waypoint
|
||||
@@ -1275,7 +1252,7 @@ uint32 ZoneDatabase::AddWPForSpawn(Client *client, uint32 spawn2id, float xpos,
|
||||
|
||||
query = StringFormat("INSERT INTO grid_entries(gridid, zoneid, `number`, x, y, z, pause, heading) "
|
||||
"VALUES (%i, %i, %i, %f, %f, %f, %i, %f)",
|
||||
grid_num, zoneid, next_wp_num, xpos, ypos, zpos, pause, heading);
|
||||
grid_num, zoneid, next_wp_num, position.m_X, position.m_Y, position.m_Z, pause, position.m_Heading);
|
||||
results = QueryDatabase(query);
|
||||
|
||||
return createdNewGrid? grid_num: 0;
|
||||
@@ -1317,16 +1294,10 @@ int ZoneDatabase::GetHighestWaypoint(uint32 zoneid, uint32 gridid) {
|
||||
|
||||
void NPC::SaveGuardSpotCharm()
|
||||
{
|
||||
guard_x_saved = guard_x;
|
||||
guard_y_saved = guard_y;
|
||||
guard_z_saved = guard_z;
|
||||
guard_heading_saved = guard_heading;
|
||||
m_GuardPointSaved = m_GuardPoint;
|
||||
}
|
||||
|
||||
void NPC::RestoreGuardSpotCharm()
|
||||
{
|
||||
guard_x = guard_x_saved;
|
||||
guard_y = guard_y_saved;
|
||||
guard_z = guard_z_saved;
|
||||
guard_heading = guard_heading_saved;
|
||||
m_GuardPoint = m_GuardPointSaved;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user