diff --git a/zone/command.cpp b/zone/command.cpp index edcd169b3..994562d06 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -439,8 +439,8 @@ int command_init(void) command_add("weather", "[0/1/2/3] (Off/Rain/Snow/Manual) - Change the weather", 80, command_weather) || command_add("who", "[search]", 20, command_who) || command_add("worldshutdown", "- Shut down world and all zones", 200, command_worldshutdown) || - command_add("wp", "[add/delete] [grid_num] [pause] [wp_num] [-h] - Add/delete a waypoint to/from a wandering grid", 170, command_wp) || - command_add("wpadd", "[pause] [-h] - Add your current location as a waypoint to your NPC target's AI path", 170, command_wpadd) || + command_add("wp", "[add|delete] [grid_id] [pause] [waypoint_id] [-h] - Add or delete a waypoint by grid ID. (-h to use current heading)", 170, command_wp) || + command_add("wpadd", "[pause] [-h] - Add your current location as a waypoint to your NPC target's AI path. (-h to use current heading)", 170, command_wpadd) || command_add("wpinfo", "- Show waypoint info about your NPC target", 170, command_wpinfo) || command_add("worldwide", "Performs world-wide GM functions such as cast (can be extended for other commands). Use caution", 250, command_worldwide) || command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", 250, command_xtargets) || @@ -2470,28 +2470,58 @@ void command_setlsinfo(Client *c, const Seperator *sep) void command_grid(Client *c, const Seperator *sep) { - if (strcasecmp("max", sep->arg[1]) == 0) { - c->Message(Chat::White, "Highest grid ID in this zone: %d", content_db.GetHighestGrid(zone->GetZoneID())); - } - else if (strcasecmp("add", sep->arg[1]) == 0) { - content_db.ModifyGrid(c, false, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), zone->GetZoneID()); - } - else if (strcasecmp("show", sep->arg[1]) == 0) { - - Mob *target = c->GetTarget(); - - if (!target || !target->IsNPC()) { - c->Message(Chat::White, "You need a NPC target!"); + auto command_type = sep->arg[1]; + auto zone_id = zone->GetZoneID(); + if (strcasecmp("max", command_type) == 0) { + c->Message( + Chat::White, + fmt::format( + "Highest grid ID in this zone is {}.", + content_db.GetHighestGrid(zone_id) + ).c_str() + ); + } else if (strcasecmp("add", command_type) == 0) { + auto grid_id = atoi(sep->arg[2]); + auto wander_type = atoi(sep->arg[3]); + auto pause_type = atoi(sep->arg[4]); + if (!content_db.GridExistsInZone(zone_id, grid_id)) { + content_db.ModifyGrid(c, false, grid_id, wander_type, pause_type, zone_id); + c->Message( + Chat::White, + fmt::format( + "Grid {} added to zone ID {} with wander type {} and pause type {}.", + grid_id, + zone_id, + wander_type, + pause_type + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "Grid {} already exists in zone ID {}.", + grid_id, + zone_id + ).c_str() + ); return; } - - std::string query = StringFormat( + } else if (strcasecmp("show", command_type) == 0) { + Mob *target = c->GetTarget(); + if (!target || !target->IsNPC()) { + c->Message(Chat::White, "You need to target an NPC!"); + return; + } + + auto grid_id = target->CastToNPC()->GetGrid(); + std::string query = fmt::format( "SELECT `x`, `y`, `z`, `heading`, `number` " "FROM `grid_entries` " - "WHERE `zoneid` = %u and `gridid` = %i " + "WHERE `zoneid` = {} AND `gridid` = {} " "ORDER BY `number`", - zone->GetZoneID(), - target->CastToNPC()->GetGrid() + zone_id, + grid_id ); auto results = content_db.QueryDatabase(query); @@ -2501,33 +2531,21 @@ void command_grid(Client *c, const Seperator *sep) } if (results.RowCount() == 0) { - c->Message(Chat::White, "No grid found"); + c->Message(Chat::White, "No grid found."); return; } - /** - * Depop any node npc's already spawned - */ - auto &mob_list = entity_list.GetMobList(); - for (auto itr = mob_list.begin(); itr != mob_list.end(); ++itr) { - Mob *mob = itr->second; - if (mob->IsNPC() && mob->GetRace() == 2254) { - mob->Depop(); - } - } + // Depop any node npc's already spawned + entity_list.DespawnGridNodes(grid_id); - /** - * Spawn grid nodes - */ + // Spawn grid nodes std::map, int32> zoffset; - - for (auto row = results.begin(); row != results.end(); ++row) { + for (auto row : results) { glm::vec4 node_position = glm::vec4(atof(row[0]), atof(row[1]), atof(row[2]), atof(row[3])); - std::vector node_loc { - node_position.x, - node_position.y, - node_position.z + node_position.x, + node_position.y, + node_position.z }; // If we already have a node at this location, set the z offset @@ -2536,46 +2554,98 @@ void command_grid(Client *c, const Seperator *sep) auto search = zoffset.find(node_loc); if (search != zoffset.end()) { search->second = search->second + 3; - } - else { + } else { zoffset[node_loc] = 0.0; } node_position.z += zoffset[node_loc]; - - NPC::SpawnGridNodeNPC(node_position,atoi(row[4]),zoffset[node_loc]); + NPC::SpawnGridNodeNPC(node_position, grid_id, atoi(row[4]), zoffset[node_loc]); } - } - else if (strcasecmp("delete", sep->arg[1]) == 0) { - content_db.ModifyGrid(c, true, atoi(sep->arg[2]), 0, 0, zone->GetZoneID()); - } - else { - c->Message(Chat::White, "Usage: #grid add/delete grid_num wandertype pausetype"); - c->Message(Chat::White, "Usage: #grid max - displays the highest grid ID used in this zone (for add)"); - c->Message(Chat::White, "Usage: #grid show - displays wp nodes as boxes"); + c->Message( + Chat::White, + fmt::format( + "Spawning nodes for grid {}.", + grid_id + ).c_str() + ); + } else if (strcasecmp("hide", command_type) == 0) { + Mob* target = c->GetTarget(); + if (!target || !target->IsNPC()) { + c->Message(Chat::White, "You need to target an NPC!"); + return; + } + + auto grid_id = target->CastToNPC()->GetGrid(); + entity_list.DespawnGridNodes(grid_id); + c->Message( + Chat::White, + fmt::format( + "Depawning nodes for grid {}.", + grid_id + ).c_str() + ); + } else if (strcasecmp("delete", command_type) == 0) { + auto grid_id = atoi(sep->arg[2]); + content_db.ModifyGrid(c, true, grid_id, 0, 0, zone_id); + c->Message( + Chat::White, + fmt::format( + "Grid {} deleted from zone ID {}.", + grid_id, + zone_id + ).c_str() + ); + } else { + c->Message(Chat::White, "Usage: #grid [add|delete] [grid_id] [wander_type] [pause_type]"); + c->Message(Chat::White, "Usage: #grid [max] - displays the highest grid ID used in this zone (for add)"); + c->Message(Chat::White, "Usage: #grid [show] - displays wp nodes as boxes"); } } void command_wp(Client *c, const Seperator *sep) { - int wp = atoi(sep->arg[4]); + auto command_type = sep->arg[1]; + auto grid_id = atoi(sep->arg[2]); + if (grid_id != 0) { + auto pause = atoi(sep->arg[3]); + auto waypoint = atoi(sep->arg[4]); + auto zone_id = zone->GetZoneID(); + if (strcasecmp("add", command_type) == 0) { + if (waypoint == 0) { // Default to highest if it's left blank, or we enter 0 + waypoint = (content_db.GetHighestWaypoint(zone_id, grid_id) + 1); + } - if (strcasecmp("add", sep->arg[1]) == 0) { - if (wp == 0) //default to highest if it's left blank, or we enter 0 - wp = content_db.GetHighestWaypoint(zone->GetZoneID(), atoi(sep->arg[2])) + 1; - if (strcasecmp("-h", sep->arg[5]) == 0) { - content_db.AddWP(c, atoi(sep->arg[2]),wp, c->GetPosition(), atoi(sep->arg[3]),zone->GetZoneID()); - } - else { - auto position = c->GetPosition(); - position.w = -1; - content_db.AddWP(c, atoi(sep->arg[2]),wp, position, atoi(sep->arg[3]),zone->GetZoneID()); + if (strcasecmp("-h", sep->arg[5]) == 0) { + content_db.AddWP(c, grid_id, waypoint, c->GetPosition(), pause, zone_id); + } else { + auto position = c->GetPosition(); + position.w = -1; + content_db.AddWP(c, grid_id, waypoint, position, pause, zone_id); + } + c->Message( + Chat::White, + fmt::format( + "Waypoint {} added to grid {} with a pause of {} {}.", + waypoint, + grid_id, + pause, + (pause == 1 ? "second" : "seconds") + ).c_str() + ); + } else if (strcasecmp("delete", command_type) == 0) { + content_db.DeleteWaypoint(c, grid_id, waypoint, zone_id); + c->Message( + Chat::White, + fmt::format( + "Waypoint {} deleted from grid {}.", + waypoint, + grid_id + ).c_str() + ); } + } else { + c->Message(Chat::White,"Usage: #wp [add|delete] [grid_id] [pause] [waypoint_id] [-h]"); } - else if (strcasecmp("delete", sep->arg[1]) == 0) - content_db.DeleteWaypoint(c, atoi(sep->arg[2]),wp,zone->GetZoneID()); - else - c->Message(Chat::White,"Usage: #wp add/delete grid_num pause wp_num [-h]"); } void command_iplookup(Client *c, const Seperator *sep) @@ -7843,20 +7913,14 @@ void command_wpinfo(Client *c, const Seperator *sep) void command_wpadd(Client *c, const Seperator *sep) { - int type1 = 0, - type2 = 0, - pause = 0; // Defaults for a new grid - + int type1 = 0, type2 = 0, pause = 0; // Defaults for a new grid Mob *target = c->GetTarget(); if (target && target->IsNPC()) { Spawn2 *s2info = target->CastToNPC()->respawn2; - - if (s2info == - nullptr) // Can't figure out where this mob's spawn came from... maybe a dynamic mob created by #spawn - { + if (s2info == nullptr) { c->Message( Chat::White, - "#wpadd FAILED -- Can't determine which spawn record in the database this mob came from!" + "#wpadd Failed, you must target a valid spawn." ); return; } @@ -7864,8 +7928,7 @@ void command_wpadd(Client *c, const Seperator *sep) if (sep->arg[1][0]) { if (atoi(sep->arg[1]) >= 0) { pause = atoi(sep->arg[1]); - } - else { + } else { c->Message(Chat::White, "Usage: #wpadd [pause] [-h]"); return; } @@ -7875,18 +7938,23 @@ void command_wpadd(Client *c, const Seperator *sep) position.w = -1; } - uint32 tmp_grid = content_db.AddWPForSpawn(c, s2info->GetID(), position, pause, type1, type2, zone->GetZoneID()); + auto zone_id = zone->GetZoneID(); + uint32 tmp_grid = content_db.AddWPForSpawn(c, s2info->GetID(), position, pause, type1, type2, zone_id); if (tmp_grid) { target->CastToNPC()->SetGrid(tmp_grid); } - target->CastToNPC()->AssignWaypoints(target->CastToNPC()->GetGrid()); + auto grid_id = target->CastToNPC()->GetGrid(); + target->CastToNPC()->AssignWaypoints(grid_id); c->Message( Chat::White, - "Waypoint added. Use #wpinfo to see waypoints for this NPC (may need to #repop first)." + fmt::format( + "Waypoint added to grid {} in zone ID {}. Use #wpinfo to see waypoints for this NPC (may need to #repop first).", + grid_id, + zone_id + ).c_str() ); - } - else { + } else { c->Message(Chat::White, "You must target an NPC to use this."); } } diff --git a/zone/entity.cpp b/zone/entity.cpp index 81087da21..53477e5d7 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -5314,3 +5314,12 @@ int EntityList::MovePlayerCorpsesToGraveyard(bool force_move_from_instance) return moved_count; } + +void EntityList::DespawnGridNodes(int32 grid_id) { + for (auto mob_iterator : mob_list) { + Mob *mob = mob_iterator.second; + if (mob->IsNPC() && mob->GetRace() == 2254 && mob->EntityVariableExists("grid_id") && atoi(mob->GetEntityVariable("grid_id")) == grid_id) { + mob->Depop(); + } + } +} diff --git a/zone/entity.h b/zone/entity.h index 5eec26f35..1690799a1 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -234,6 +234,7 @@ public: void RemoveAllCorpsesByCharID(uint32 charid); void RemoveCorpseByDBID(uint32 dbid); int RezzAllCorpsesByCharID(uint32 charid); + void DespawnGridNodes(int32 grid_id); bool IsMobInZone(Mob *who); void ClearClientPetitionQueue(); bool CanAddHateForMob(Mob *p); diff --git a/zone/npc.cpp b/zone/npc.cpp index 325f8fb70..3d65eed93 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1099,8 +1099,7 @@ bool NPC::SpawnZoneController() return true; } -void NPC::SpawnGridNodeNPC(const glm::vec4 &position, int32 grid_number, int32 zoffset) { - +void NPC::SpawnGridNodeNPC(const glm::vec4 &position, int32 grid_id, int32 grid_number, int32 zoffset) { auto npc_type = new NPCType; memset(npc_type, 0, sizeof(NPCType)); @@ -1112,31 +1111,30 @@ void NPC::SpawnGridNodeNPC(const glm::vec4 &position, int32 grid_number, int32 z strcat(npc_type->name, "(Stacked)"); } - npc_type->current_hp = 4000000; - npc_type->max_hp = 4000000; - npc_type->race = 2254; - npc_type->gender = 2; - npc_type->class_ = 9; - npc_type->deity = 1; - npc_type->level = 200; - npc_type->npc_id = 0; - npc_type->loottable_id = 0; - npc_type->texture = 1; - npc_type->light = 1; - npc_type->size = 1; - npc_type->runspeed = 0; - npc_type->merchanttype = 1; - npc_type->bodytype = 1; - npc_type->show_name = true; - npc_type->findable = true; + npc_type->current_hp = 4000000; + npc_type->max_hp = 4000000; + npc_type->race = 2254; + npc_type->gender = 2; + npc_type->class_ = 9; + npc_type->deity = 1; + npc_type->level = 200; + npc_type->npc_id = 0; + npc_type->loottable_id = 0; + npc_type->texture = 1; + npc_type->light = 1; + npc_type->size = 1; + npc_type->runspeed = 0; + npc_type->merchanttype = 1; + npc_type->bodytype = 1; + npc_type->show_name = true; + npc_type->findable = true; + strn0cpy(npc_type->special_abilities, "24,1^35,1", 512); auto node_position = glm::vec4(position.x, position.y, position.z, position.w); - auto npc = new NPC(npc_type, nullptr, node_position, GravityBehavior::Flying); - - npc->name[strlen(npc->name)-3] = (char) NULL; - + auto npc = new NPC(npc_type, nullptr, node_position, GravityBehavior::Flying); + npc->name[strlen(npc->name) - 3] = (char) NULL; npc->GiveNPCTypeData(npc_type); - + npc->SetEntityVariable("grid_id", itoa(grid_id)); entity_list.AddNPC(npc); } diff --git a/zone/npc.h b/zone/npc.h index 121503215..d75fe67f7 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -114,7 +114,7 @@ public: virtual ~NPC(); static NPC *SpawnNodeNPC(std::string name, std::string last_name, const glm::vec4 &position); - static void SpawnGridNodeNPC(const glm::vec4 &position, int32 grid_number, int32 zoffset); + static void SpawnGridNodeNPC(const glm::vec4 &position, int32 grid_id, int32 grid_number, int32 zoffset); static void SpawnZonePointNodeNPC(std::string name, const glm::vec4 &position); //abstract virtual function implementations requird by base abstract class diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 90fc25d1e..76db92dd6 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -1036,6 +1036,24 @@ void ZoneDatabase::ModifyGrid(Client *client, bool remove, uint32 id, uint8 type results = QueryDatabase(query); } +bool ZoneDatabase::GridExistsInZone(uint32 zone_id, uint32 grid_id) { + bool grid_exists = false; + std::string query = fmt::format( + "SELECT * FROM `grid` WHERE `id` = {} AND `zoneid` = {}", + grid_id, + zone_id + ); + auto results = QueryDatabase(query); + if (!results.Success()) { + return grid_exists; + } + + if (results.RowCount() == 1) { + grid_exists = true; + } + return grid_exists; +} + /************************************** * AddWP - Adds a new waypoint to a specific grid for a specific zone. */ diff --git a/zone/zonedb.h b/zone/zonedb.h index e05e0872a..7f523e4e1 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -448,6 +448,7 @@ public: void AddWP(Client *c, uint32 gridid, uint32 wpnum, const glm::vec4& position, uint32 pause, uint16 zoneid); uint32 AddWPForSpawn(Client *c, uint32 spawn2id, const glm::vec4& position, uint32 pause, int type1, int type2, uint16 zoneid); void ModifyGrid(Client *c, bool remove, uint32 id, uint8 type = 0, uint8 type2 = 0, uint16 zoneid = 0); + bool GridExistsInZone(uint32 zone_id, uint32 grid_id); void ModifyWP(Client *c, uint32 grid_id, uint32 wp_num, const glm::vec3& location, uint32 script = 0, uint16 zoneid = 0); uint8 GetGridType(uint32 grid, uint32 zoneid); uint8 GetGridType2(uint32 grid, uint16 zoneid);