Merge pull request #956 from regneq/master

New pathgrid types.  fixed an issue where npc would face north when pause and heading were set at -1.
This commit is contained in:
Michael Cook (mackal) 2020-01-24 20:31:10 -05:00 committed by GitHub
commit f73f72b2b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 219 additions and 43 deletions

View File

@ -400,6 +400,7 @@
9144|2019_11_09_logsys_description_update.sql|SELECT * FROM db_version WHERE version >= 9143|empty| 9144|2019_11_09_logsys_description_update.sql|SELECT * FROM db_version WHERE version >= 9143|empty|
9145|2019_12_24_banned_ips_update.sql|SHOW TABLES LIKE 'Banned_IPs'|not_empty| 9145|2019_12_24_banned_ips_update.sql|SHOW TABLES LIKE 'Banned_IPs'|not_empty|
9146|2020_01_10_character_soft_deletes.sql|SHOW COLUMNS FROM `character_data` LIKE 'deleted_at'|empty| 9146|2020_01_10_character_soft_deletes.sql|SHOW COLUMNS FROM `character_data` LIKE 'deleted_at'|empty|
9147|2020_01_24_grid_centerpoint_wp.sql|SHOW COLUMNS FROM `grid_entries` LIKE 'centerpoint'|empty|
# Upgrade conditions: # Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not # This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,2 @@
alter table grid_entries add column `centerpoint` tinyint(4) not null default 0;
alter table spawngroup add column `wp_spawns` tinyint(1) unsigned not null default 0;

View File

@ -647,6 +647,19 @@ enum {
SKILLUP_FAILURE = 2 SKILLUP_FAILURE = 2
}; };
enum {
GridCircular,
GridRandom10,
GridRandom,
GridPatrol,
GridOneWayRepop,
GridRand5LoS,
GridOneWayDepop,
GridCenterPoint,
GridRandomCenterPoint,
GridRandomPath
};
typedef enum { typedef enum {
petFamiliar, //only listens to /pet get lost petFamiliar, //only listens to /pet get lost
petAnimation, //does not listen to any commands petAnimation, //does not listen to any commands

View File

@ -1693,9 +1693,29 @@ void NPC::AI_DoMovement() {
GetZ(), GetZ(),
GetGrid()); GetGrid());
if (wandertype == GridRandomPath)
{
if (cur_wp == patrol)
{
// reached our randomly selected destination; force a pause
if (cur_wp_pause == 0)
{
if (Waypoints.size() > 0 && Waypoints[0].pause)
cur_wp_pause = Waypoints[0].pause;
else
cur_wp_pause = 38;
}
Log(Logs::Detail, Logs::AI, "NPC using wander type GridRandomPath on grid %d at waypoint %d has reached its random destination; pause time is %d", GetGrid(), cur_wp, cur_wp_pause);
}
else
cur_wp_pause = 0; // skipping pauses until destination
}
SetWaypointPause(); SetWaypointPause();
if (GetAppearance() != eaStanding) {
SetAppearance(eaStanding, false); SetAppearance(eaStanding, false);
if (cur_wp_pause > 0) { }
if (cur_wp_pause > 0 && m_CurrentWayPoint.w >= 0.0) {
RotateTo(m_CurrentWayPoint.w); RotateTo(m_CurrentWayPoint.w);
} }
@ -1789,12 +1809,12 @@ void NPC::AI_SetupNextWaypoint() {
} }
} }
if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { if (wandertype == GridOneWayRepop && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(true); //depop and restart spawn timer CastToNPC()->Depop(true); //depop and restart spawn timer
if (found_spawn) if (found_spawn)
found_spawn->SetNPCPointerNull(); found_spawn->SetNPCPointerNull();
} }
else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { else if (wandertype == GridOneWayDepop && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(false);//depop without spawn timer CastToNPC()->Depop(false);//depop without spawn timer
if (found_spawn) if (found_spawn)
found_spawn->SetNPCPointerNull(); found_spawn->SetNPCPointerNull();

View File

@ -303,7 +303,7 @@ public:
int GetMaxWp() const { return max_wp; } int GetMaxWp() const { return max_wp; }
void DisplayWaypointInfo(Client *to); void DisplayWaypointInfo(Client *to);
void CalculateNewWaypoint(); void CalculateNewWaypoint();
void AssignWaypoints(int32 grid); void AssignWaypoints(int32 grid, int start_wp = 0);
void SetWaypointPause(); void SetWaypointPause();
void UpdateWaypoint(int wp_index); void UpdateWaypoint(int wp_index);
@ -312,7 +312,8 @@ public:
void ResumeWandering(); void ResumeWandering();
void PauseWandering(int pausetime); void PauseWandering(int pausetime);
void MoveTo(const glm::vec4& position, bool saveguardspot); void MoveTo(const glm::vec4& position, bool saveguardspot);
void GetClosestWaypoint(std::list<wplist> &wp_list, int count, const glm::vec3& location); void GetClosestWaypoints(std::list<wplist> &wp_list, int count, const glm::vec3& location);
int GetClosestWaypoint(const glm::vec3& location);
uint32 GetEquippedItemFromTextureSlot(uint8 material_slot) const; // returns item id uint32 GetEquippedItemFromTextureSlot(uint8 material_slot) const; // returns item id
int32 GetEquipmentMaterial(uint8 material_slot) const; int32 GetEquipmentMaterial(uint8 material_slot) const;

View File

@ -233,6 +233,20 @@ bool Spawn2::Process() {
} }
currentnpcid = npcid; currentnpcid = npcid;
glm::vec4 loc(x, y, z, heading);
int starting_wp = 0;
if (spawn_group->wp_spawns && grid_ > 0)
{
glm::vec4 wploc;
starting_wp = database.GetRandomWaypointLocFromGrid(wploc, zone->GetZoneID(), grid_);
if (wploc.x != 0.0f || wploc.y != 0.0f || wploc.z != 0.0f)
{
loc = wploc;
Log(Logs::General, Logs::Spawns, "spawning at random waypoint #%i loc: (%.3f, %.3f, %.3f).", starting_wp , loc.x, loc.y, loc.z);
}
}
NPC *npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), GravityBehavior::Water); NPC *npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), GravityBehavior::Water);
npc->mod_prespawn(this); npc->mod_prespawn(this);
@ -275,7 +289,7 @@ bool Spawn2::Process() {
z z
); );
LoadGrid(); LoadGrid(starting_wp);
} }
else { else {
LogSpawns("Spawn2 [{}]: Group [{}] spawned [{}] ([{}]) at ([{}], [{}], [{}]). Grid loading delayed", LogSpawns("Spawn2 [{}]: Group [{}] spawned [{}] ([{}]) at ([{}], [{}], [{}]). Grid loading delayed",
@ -302,7 +316,7 @@ void Spawn2::Disable()
enabled = false; enabled = false;
} }
void Spawn2::LoadGrid() { void Spawn2::LoadGrid(int start_wp) {
if (!npcthis) if (!npcthis)
return; return;
if (grid_ < 1) if (grid_ < 1)
@ -311,8 +325,8 @@ void Spawn2::LoadGrid() {
return; return;
//dont set an NPC's grid until its loaded for them. //dont set an NPC's grid until its loaded for them.
npcthis->SetGrid(grid_); npcthis->SetGrid(grid_);
npcthis->AssignWaypoints(grid_); npcthis->AssignWaypoints(grid_, start_wp);
LogSpawns("Spawn2 [{}]: Loading grid [{}] for [{}]", spawn2_id, grid_, npcthis->GetName()); LogSpawns("Spawn2 [{}]: Loading grid [{}] for [{}]; starting wp is [{}]", spawn2_id, grid_, npcthis->GetName(), start_wp);
} }
/* /*

View File

@ -36,7 +36,7 @@ public:
uint16 cond_id = SC_AlwaysEnabled, int16 min_value = 0, bool in_enabled = true, EmuAppearance anim = eaStanding); uint16 cond_id = SC_AlwaysEnabled, int16 min_value = 0, bool in_enabled = true, EmuAppearance anim = eaStanding);
~Spawn2(); ~Spawn2();
void LoadGrid(); void LoadGrid(int start_wp = 0);
void Enable() { enabled = true; } void Enable() { enabled = true; }
void Disable(); void Disable();
bool Enabled() { return enabled; } bool Enabled() { return enabled; }

View File

@ -48,7 +48,8 @@ SpawnGroup::SpawnGroup(
int delay_in, int delay_in,
int despawn_in, int despawn_in,
uint32 despawn_timer_in, uint32 despawn_timer_in,
int min_delay_in int min_delay_in,
bool wp_spawns_in
) )
{ {
id = in_id; id = in_id;
@ -63,6 +64,7 @@ SpawnGroup::SpawnGroup(
delay = delay_in; delay = delay_in;
despawn = despawn_in; despawn = despawn_in;
despawn_timer = despawn_timer_in; despawn_timer = despawn_timer_in;
wp_spawns = wp_spawns_in;
} }
uint32 SpawnGroup::GetNPCType(uint16 in_filter) uint32 SpawnGroup::GetNPCType(uint16 in_filter)
@ -198,7 +200,8 @@ bool ZoneDatabase::LoadSpawnGroups(const char *zone_name, uint16 version, SpawnG
spawngroup.delay, spawngroup.delay,
spawngroup.despawn, spawngroup.despawn,
spawngroup.despawn_timer, spawngroup.despawn_timer,
spawngroup.mindelay spawngroup.mindelay,
spawngroup.wp_spawns
FROM FROM
spawn2, spawn2,
spawngroup spawngroup
@ -229,7 +232,8 @@ bool ZoneDatabase::LoadSpawnGroups(const char *zone_name, uint16 version, SpawnG
atoi(row[8]), atoi(row[8]),
atoi(row[9]), atoi(row[9]),
atoi(row[10]), atoi(row[10]),
atoi(row[11]) atoi(row[11]),
atoi(row[12])
); );
spawn_group_list->AddSpawnGroup(new_spawn_group); spawn_group_list->AddSpawnGroup(new_spawn_group);
@ -305,7 +309,8 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawn_group_id, SpawnGroupList *spawn
spawngroup.delay, spawngroup.delay,
spawngroup.despawn, spawngroup.despawn,
spawngroup.despawn_timer, spawngroup.despawn_timer,
spawngroup.mindelay spawngroup.mindelay,
spawngroup.wp_spawns
FROM FROM
spawngroup spawngroup
WHERE WHERE
@ -332,7 +337,8 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawn_group_id, SpawnGroupList *spawn
atoi(row[8]), atoi(row[8]),
atoi(row[9]), atoi(row[9]),
atoi(row[10]), atoi(row[10]),
atoi(row[11]) atoi(row[11]),
atoi(row[12])
); );
spawn_group_list->AddSpawnGroup(new_spawn_group); spawn_group_list->AddSpawnGroup(new_spawn_group);

View File

@ -49,13 +49,15 @@ public:
int delay_in, int delay_in,
int despawn_in, int despawn_in,
uint32 despawn_timer_in, uint32 despawn_timer_in,
int min_delay_in int min_delay_in,
bool wp_spawns_in
); );
~SpawnGroup(); ~SpawnGroup();
uint32 GetNPCType(uint16 condition_value_filter=1); uint32 GetNPCType(uint16 condition_value_filter=1);
void AddSpawnEntry(SpawnEntry *newEntry); void AddSpawnEntry(SpawnEntry *newEntry);
uint32 id; uint32 id;
bool wp_spawns; // if true, spawn NPCs at a random waypoint location (if spawnpoint has a grid) instead of the spawnpoint's loc
float roamdist; float roamdist;
float roambox[4]; float roambox[4];
int min_delay; int min_delay;

View File

@ -244,14 +244,14 @@ void NPC::CalculateNewWaypoint()
int old_wp = cur_wp; int old_wp = cur_wp;
bool reached_end = false; bool reached_end = false;
bool reached_beginning = false; bool reached_beginning = false;
if (cur_wp == max_wp) if (cur_wp == max_wp - 1) //cur_wp starts at 0, max_wp starts at 1.
reached_end = true; reached_end = true;
if (cur_wp == 0) if (cur_wp == 0)
reached_beginning = true; reached_beginning = true;
switch (wandertype) switch (wandertype)
{ {
case 0: //circle case GridCircular:
{ {
if (reached_end) if (reached_end)
cur_wp = 0; cur_wp = 0;
@ -259,10 +259,10 @@ void NPC::CalculateNewWaypoint()
cur_wp = cur_wp + 1; cur_wp = cur_wp + 1;
break; break;
} }
case 1: //10 closest case GridRandom10:
{ {
std::list<wplist> closest; std::list<wplist> closest;
GetClosestWaypoint(closest, 10, glm::vec3(GetPosition())); GetClosestWaypoints(closest, 10, glm::vec3(GetPosition()));
auto iter = closest.begin(); auto iter = closest.begin();
if (closest.size() != 0) if (closest.size() != 0)
{ {
@ -273,10 +273,17 @@ void NPC::CalculateNewWaypoint()
break; break;
} }
case 2: //random case GridRandom:
case GridCenterPoint:
{
if (wandertype == GridCenterPoint && !reached_beginning)
{
cur_wp = 0;
}
else
{ {
cur_wp = zone->random.Int(0, Waypoints.size() - 1); cur_wp = zone->random.Int(0, Waypoints.size() - 1);
if (cur_wp == old_wp) if (cur_wp == old_wp || (wandertype == GridCenterPoint && cur_wp == 0))
{ {
if (cur_wp == (Waypoints.size() - 1)) if (cur_wp == (Waypoints.size() - 1))
{ {
@ -293,10 +300,37 @@ void NPC::CalculateNewWaypoint()
} }
} }
} }
}
break; break;
} }
case 3: //patrol case GridRandomCenterPoint:
{
bool on_center = Waypoints[cur_wp].centerpoint;
std::vector<wplist> random_waypoints;
for (auto &w : Waypoints)
{
wplist wpl = w;
if (wpl.index != cur_wp &&
((on_center && !wpl.centerpoint) || (!on_center && wpl.centerpoint)))
{
random_waypoints.push_back(w);
}
}
if (random_waypoints.size() == 0)
{
cur_wp = 0;
}
else
{
int windex = zone->random.Roll0(random_waypoints.size());
cur_wp = random_waypoints[windex].index;
}
break;
}
case GridPatrol:
{ {
if (reached_end) if (reached_end)
patrol = 1; patrol = 1;
@ -309,16 +343,16 @@ void NPC::CalculateNewWaypoint()
break; break;
} }
case 4: //goto the end and depop with spawn timer case GridOneWayRepop:
case 6: //goto the end and depop without spawn timer case GridOneWayDepop:
{ {
cur_wp = cur_wp + 1; cur_wp = cur_wp + 1;
break; break;
} }
case 5: //pick random closest 5 and pick one that's in sight case GridRand5LoS:
{ {
std::list<wplist> closest; std::list<wplist> closest;
GetClosestWaypoint(closest, 5, glm::vec3(GetPosition())); GetClosestWaypoints(closest, 5, glm::vec3(GetPosition()));
auto iter = closest.begin(); auto iter = closest.begin();
while (iter != closest.end()) while (iter != closest.end())
@ -341,6 +375,25 @@ void NPC::CalculateNewWaypoint()
} }
break; break;
} }
case GridRandomPath: // randomly select a waypoint but follow path to it instead of walk directly to it ignoring walls
{
if (Waypoints.size() == 0)
{
cur_wp = 0;
}
else
{
if (cur_wp == patrol) // reutilizing patrol member instead of making new member for this wander type; here we use it to save a random waypoint
{
while (patrol == cur_wp)
patrol = zone->random.Int(0, Waypoints.size() - 1);
}
if (patrol > cur_wp)
cur_wp = cur_wp + 1;
else
cur_wp = cur_wp - 1;
}
}
} }
// Preserve waypoint setting for quest controlled NPCs // Preserve waypoint setting for quest controlled NPCs
@ -357,7 +410,30 @@ bool wp_distance_pred(const wp_distance& left, const wp_distance& right)
return left.dist < right.dist; return left.dist < right.dist;
} }
void NPC::GetClosestWaypoint(std::list<wplist> &wp_list, int count, const glm::vec3& location) int NPC::GetClosestWaypoint(const glm::vec3& location)
{
if (Waypoints.size() <= 1)
return 0;
int closest = 0;
float closestDist = 9999999.0f;
float dist;
for (int i = 0; i < Waypoints.size(); ++i)
{
dist = DistanceSquared(location, glm::vec3(Waypoints[i].x, Waypoints[i].y, Waypoints[i].z));
if (dist < closestDist)
{
closestDist = dist;
closest = i;
}
}
return closest;
}
// fills wp_list with the closest count number of waypoints
void NPC::GetClosestWaypoints(std::list<wplist> &wp_list, int count, const glm::vec3& location)
{ {
wp_list.clear(); wp_list.clear();
if (Waypoints.size() <= count) if (Waypoints.size() <= count)
@ -485,7 +561,7 @@ void Mob::StopNavigation() {
mMovementManager->StopNavigation(this); mMovementManager->StopNavigation(this);
} }
void NPC::AssignWaypoints(int32 grid) void NPC::AssignWaypoints(int32 grid, int start_wp)
{ {
if (grid == 0) if (grid == 0)
return; // grid ID 0 not supported return; // grid ID 0 not supported
@ -518,7 +594,7 @@ void NPC::AssignWaypoints(int32 grid)
SetGrid(grid); // Assign grid number SetGrid(grid); // Assign grid number
// Retrieve all waypoints for this grid // Retrieve all waypoints for this grid
query = StringFormat("SELECT `x`,`y`,`z`,`pause`,`heading` " query = StringFormat("SELECT `x`,`y`,`z`,`pause`,`heading`, `centerpoint` "
"FROM grid_entries WHERE `gridid` = %i AND `zoneid` = %i " "FROM grid_entries WHERE `gridid` = %i AND `zoneid` = %i "
"ORDER BY `number`", grid, zone->GetZoneID()); "ORDER BY `number`", grid, zone->GetZoneID());
results = database.QueryDatabase(query); results = database.QueryDatabase(query);
@ -539,14 +615,22 @@ void NPC::AssignWaypoints(int32 grid)
newwp.pause = atoi(row[3]); newwp.pause = atoi(row[3]);
newwp.heading = atof(row[4]); newwp.heading = atof(row[4]);
newwp.centerpoint = atobool(row[5]);
Waypoints.push_back(newwp); Waypoints.push_back(newwp);
} }
UpdateWaypoint(0); cur_wp = start_wp;
UpdateWaypoint(start_wp);
SetWaypointPause(); SetWaypointPause();
if (wandertype == 1 || wandertype == 2 || wandertype == 5) if (wandertype == GridRandomPath) {
cur_wp = GetClosestWaypoint(glm::vec3(GetPosition()));
patrol = cur_wp;
}
if (wandertype == GridRandom10 || wandertype == GridRandom || wandertype == GridRand5LoS)
CalculateNewWaypoint(); CalculateNewWaypoint();
} }
void Mob::SendTo(float new_x, float new_y, float new_z) { void Mob::SendTo(float new_x, float new_y, float new_z) {
@ -1058,6 +1142,37 @@ int ZoneDatabase::GetHighestWaypoint(uint32 zoneid, uint32 gridid) {
return atoi(row[0]); return atoi(row[0]);
} }
int ZoneDatabase::GetRandomWaypointLocFromGrid(glm::vec4 &loc, uint16 zoneid, int grid)
{
loc.x = loc.y = loc.z = loc.w = 0.0f;
std::string query = StringFormat("SELECT `x`,`y`,`z`,`heading` "
"FROM grid_entries WHERE `gridid` = %i AND `zoneid` = %u ORDER BY `number`", grid, zone->GetZoneID());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
Log(Logs::General, Logs::Error, "MySQL Error while trying get random waypoint loc from grid %i in zoneid %u; %s", grid, zoneid, results.ErrorMessage().c_str());
return 0;
}
if (results.RowCount() > 0)
{
int roll = zone->random.Int(0, results.RowCount() - 1);
int i = 0;
auto row = results.begin();
while (i < roll)
{
row++;
i++;
}
loc.x = atof(row[0]);
loc.y = atof(row[1]);
loc.z = atof(row[2]);
loc.w = atof(row[3]);
return i;
}
return 0;
}
void NPC::SaveGuardSpotCharm() void NPC::SaveGuardSpotCharm()
{ {
m_GuardPointSaved = m_GuardPoint; m_GuardPointSaved = m_GuardPoint;

View File

@ -47,6 +47,7 @@ struct wplist {
float z; float z;
int pause; int pause;
float heading; float heading;
bool centerpoint;
}; };
#pragma pack(1) #pragma pack(1)
@ -434,6 +435,7 @@ public:
void AssignGrid(Client *client, int grid, int spawn2id); void AssignGrid(Client *client, int grid, int spawn2id);
int GetHighestGrid(uint32 zoneid); int GetHighestGrid(uint32 zoneid);
int GetHighestWaypoint(uint32 zoneid, uint32 gridid); int GetHighestWaypoint(uint32 zoneid, uint32 gridid);
int GetRandomWaypointLocFromGrid(glm::vec4 &loc, uint16 zoneid, int grid);
/* NPCs */ /* NPCs */