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|
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|
9147|2020_01_24_grid_centerpoint_wp.sql|SHOW COLUMNS FROM `grid_entries` LIKE 'centerpoint'|empty|
# Upgrade conditions:
# 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
};
enum {
GridCircular,
GridRandom10,
GridRandom,
GridPatrol,
GridOneWayRepop,
GridRand5LoS,
GridOneWayDepop,
GridCenterPoint,
GridRandomCenterPoint,
GridRandomPath
};
typedef enum {
petFamiliar, //only listens to /pet get lost
petAnimation, //does not listen to any commands

View File

@ -1693,12 +1693,32 @@ void NPC::AI_DoMovement() {
GetZ(),
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();
SetAppearance(eaStanding, false);
if (cur_wp_pause > 0) {
if (GetAppearance() != eaStanding) {
SetAppearance(eaStanding, false);
}
if (cur_wp_pause > 0 && m_CurrentWayPoint.w >= 0.0) {
RotateTo(m_CurrentWayPoint.w);
}
//kick off event_waypoint arrive
char temp[16];
sprintf(temp, "%d", cur_wp);
@ -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
if (found_spawn)
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
if (found_spawn)
found_spawn->SetNPCPointerNull();

View File

@ -303,7 +303,7 @@ public:
int GetMaxWp() const { return max_wp; }
void DisplayWaypointInfo(Client *to);
void CalculateNewWaypoint();
void AssignWaypoints(int32 grid);
void AssignWaypoints(int32 grid, int start_wp = 0);
void SetWaypointPause();
void UpdateWaypoint(int wp_index);
@ -312,7 +312,8 @@ public:
void ResumeWandering();
void PauseWandering(int pausetime);
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
int32 GetEquipmentMaterial(uint8 material_slot) const;

View File

@ -233,6 +233,20 @@ bool Spawn2::Process() {
}
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->mod_prespawn(this);
@ -275,7 +289,7 @@ bool Spawn2::Process() {
z
);
LoadGrid();
LoadGrid(starting_wp);
}
else {
LogSpawns("Spawn2 [{}]: Group [{}] spawned [{}] ([{}]) at ([{}], [{}], [{}]). Grid loading delayed",
@ -302,7 +316,7 @@ void Spawn2::Disable()
enabled = false;
}
void Spawn2::LoadGrid() {
void Spawn2::LoadGrid(int start_wp) {
if (!npcthis)
return;
if (grid_ < 1)
@ -311,8 +325,8 @@ void Spawn2::LoadGrid() {
return;
//dont set an NPC's grid until its loaded for them.
npcthis->SetGrid(grid_);
npcthis->AssignWaypoints(grid_);
LogSpawns("Spawn2 [{}]: Loading grid [{}] for [{}]", spawn2_id, grid_, npcthis->GetName());
npcthis->AssignWaypoints(grid_, start_wp);
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);
~Spawn2();
void LoadGrid();
void LoadGrid(int start_wp = 0);
void Enable() { enabled = true; }
void Disable();
bool Enabled() { return enabled; }

View File

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

View File

@ -49,13 +49,15 @@ public:
int delay_in,
int despawn_in,
uint32 despawn_timer_in,
int min_delay_in
int min_delay_in,
bool wp_spawns_in
);
~SpawnGroup();
uint32 GetNPCType(uint16 condition_value_filter=1);
void AddSpawnEntry(SpawnEntry *newEntry);
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 roambox[4];
int min_delay;

View File

@ -244,14 +244,14 @@ void NPC::CalculateNewWaypoint()
int old_wp = cur_wp;
bool reached_end = 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;
if (cur_wp == 0)
reached_beginning = true;
switch (wandertype)
{
case 0: //circle
case GridCircular:
{
if (reached_end)
cur_wp = 0;
@ -259,10 +259,10 @@ void NPC::CalculateNewWaypoint()
cur_wp = cur_wp + 1;
break;
}
case 1: //10 closest
case GridRandom10:
{
std::list<wplist> closest;
GetClosestWaypoint(closest, 10, glm::vec3(GetPosition()));
GetClosestWaypoints(closest, 10, glm::vec3(GetPosition()));
auto iter = closest.begin();
if (closest.size() != 0)
{
@ -273,30 +273,64 @@ void NPC::CalculateNewWaypoint()
break;
}
case 2: //random
case GridRandom:
case GridCenterPoint:
{
cur_wp = zone->random.Int(0, Waypoints.size() - 1);
if (cur_wp == old_wp)
if (wandertype == GridCenterPoint && !reached_beginning)
{
if (cur_wp == (Waypoints.size() - 1))
cur_wp = 0;
}
else
{
cur_wp = zone->random.Int(0, Waypoints.size() - 1);
if (cur_wp == old_wp || (wandertype == GridCenterPoint && cur_wp == 0))
{
if (cur_wp > 0)
if (cur_wp == (Waypoints.size() - 1))
{
cur_wp--;
if (cur_wp > 0)
{
cur_wp--;
}
}
}
else if (cur_wp == 0)
{
if ((Waypoints.size() - 1) > 0)
else if (cur_wp == 0)
{
cur_wp++;
if ((Waypoints.size() - 1) > 0)
{
cur_wp++;
}
}
}
}
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)
patrol = 1;
@ -309,16 +343,16 @@ void NPC::CalculateNewWaypoint()
break;
}
case 4: //goto the end and depop with spawn timer
case 6: //goto the end and depop without spawn timer
case GridOneWayRepop:
case GridOneWayDepop:
{
cur_wp = cur_wp + 1;
break;
}
case 5: //pick random closest 5 and pick one that's in sight
case GridRand5LoS:
{
std::list<wplist> closest;
GetClosestWaypoint(closest, 5, glm::vec3(GetPosition()));
GetClosestWaypoints(closest, 5, glm::vec3(GetPosition()));
auto iter = closest.begin();
while (iter != closest.end())
@ -341,6 +375,25 @@ void NPC::CalculateNewWaypoint()
}
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
@ -357,7 +410,30 @@ 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, 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();
if (Waypoints.size() <= count)
@ -485,7 +561,7 @@ void Mob::StopNavigation() {
mMovementManager->StopNavigation(this);
}
void NPC::AssignWaypoints(int32 grid)
void NPC::AssignWaypoints(int32 grid, int start_wp)
{
if (grid == 0)
return; // grid ID 0 not supported
@ -518,7 +594,7 @@ void NPC::AssignWaypoints(int32 grid)
SetGrid(grid); // Assign grid number
// 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 "
"ORDER BY `number`", grid, zone->GetZoneID());
results = database.QueryDatabase(query);
@ -539,14 +615,22 @@ void NPC::AssignWaypoints(int32 grid)
newwp.pause = atoi(row[3]);
newwp.heading = atof(row[4]);
newwp.centerpoint = atobool(row[5]);
Waypoints.push_back(newwp);
}
UpdateWaypoint(0);
cur_wp = start_wp;
UpdateWaypoint(start_wp);
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();
}
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]);
}
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()
{
m_GuardPointSaved = m_GuardPoint;

View File

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