[2] | 1 | /* |
---|
[44] | 2 | * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> |
---|
[2] | 3 | * |
---|
[44] | 4 | * Thanks to the original authors: MaNGOS <http://www.mangosproject.org/> |
---|
| 5 | * |
---|
[2] | 6 | * This program is free software; you can redistribute it and/or modify |
---|
| 7 | * it under the terms of the GNU General Public License as published by |
---|
| 8 | * the Free Software Foundation; either version 2 of the License, or |
---|
| 9 | * (at your option) any later version. |
---|
| 10 | * |
---|
| 11 | * This program is distributed in the hope that it will be useful, |
---|
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
[44] | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
[2] | 14 | * GNU General Public License for more details. |
---|
| 15 | * |
---|
| 16 | * You should have received a copy of the GNU General Public License |
---|
| 17 | * along with this program; if not, write to the Free Software |
---|
[44] | 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
[2] | 19 | */ |
---|
| 20 | |
---|
| 21 | #include "Database/DatabaseEnv.h" |
---|
| 22 | #include "GridDefines.h" |
---|
| 23 | #include "Policies/SingletonImp.h" |
---|
| 24 | #include "WaypointManager.h" |
---|
| 25 | #include "ProgressBar.h" |
---|
| 26 | #include "MapManager.h" |
---|
| 27 | |
---|
| 28 | INSTANTIATE_SINGLETON_1(WaypointManager); |
---|
| 29 | |
---|
| 30 | bool WaypointBehavior::isEmpty() |
---|
| 31 | { |
---|
| 32 | return emote == 0 && spell == 0 && model1 == 0 && model2 == 0 && text[0].empty() && |
---|
| 33 | text[1].empty() && text[2].empty() && text[3].empty() && text[4].empty(); |
---|
| 34 | } |
---|
| 35 | |
---|
| 36 | WaypointBehavior::WaypointBehavior(const WaypointBehavior &b) |
---|
| 37 | { |
---|
| 38 | emote = b.emote; spell = b.spell; model1 = b.model1; model2 = b.model2; |
---|
| 39 | text[0] = b.text[0]; text[1] = b.text[1]; text[2] = b.text[2]; |
---|
| 40 | text[3] = b.text[3]; text[4] = b.text[4]; |
---|
| 41 | } |
---|
| 42 | |
---|
| 43 | void WaypointManager::Load() |
---|
| 44 | { |
---|
| 45 | Cleanup(); |
---|
| 46 | |
---|
| 47 | uint32 total_paths = 0; |
---|
| 48 | uint32 total_nodes = 0; |
---|
| 49 | uint32 total_behaviors = 0; |
---|
| 50 | |
---|
| 51 | QueryResult *result = WorldDatabase.Query("SELECT id, COUNT(point) FROM creature_movement GROUP BY id"); |
---|
| 52 | if(result) |
---|
| 53 | { |
---|
| 54 | total_paths = result->GetRowCount(); |
---|
| 55 | barGoLink bar( total_paths ); |
---|
| 56 | do |
---|
| 57 | { |
---|
| 58 | Field *fields = result->Fetch(); |
---|
| 59 | uint32 id = fields[0].GetUInt32(); |
---|
| 60 | uint32 count = fields[1].GetUInt32(); |
---|
| 61 | m_pathMap[id].resize(count); |
---|
| 62 | |
---|
| 63 | total_nodes += count; |
---|
| 64 | bar.step(); |
---|
| 65 | } while( result->NextRow() ); |
---|
| 66 | delete result; |
---|
| 67 | } |
---|
| 68 | |
---|
| 69 | result = WorldDatabase.Query("SELECT position_x, position_y, position_z, orientation, model1, model2, waittime, emote, spell, text1, text2, text3, text4, text5, id, point FROM creature_movement"); |
---|
| 70 | if(result) |
---|
| 71 | { |
---|
| 72 | barGoLink bar( result->GetRowCount() ); |
---|
| 73 | do |
---|
| 74 | { |
---|
| 75 | Field *fields = result->Fetch(); |
---|
| 76 | uint32 point = fields[15].GetUInt32(); |
---|
| 77 | uint32 id = fields[14].GetUInt32(); |
---|
| 78 | |
---|
| 79 | WaypointPath &path = m_pathMap[id]; |
---|
| 80 | // the cleanup queries make sure the following is true |
---|
| 81 | assert(point >= 1 && point <= path.size()); |
---|
| 82 | WaypointNode &node = path[point-1]; |
---|
| 83 | |
---|
| 84 | node.x = fields[0].GetFloat(); |
---|
| 85 | node.y = fields[1].GetFloat(); |
---|
| 86 | node.z = fields[2].GetFloat(); |
---|
| 87 | node.orientation = fields[3].GetFloat(); |
---|
| 88 | node.delay = fields[6].GetUInt16(); |
---|
| 89 | |
---|
| 90 | // prevent using invalid coordinates |
---|
[44] | 91 | if(!Trinity::IsValidMapCoord(node.x, node.y, node.z, node.orientation)) |
---|
[2] | 92 | { |
---|
| 93 | QueryResult *result1 = WorldDatabase.PQuery("SELECT id, map FROM creature WHERE guid = '%u'", id); |
---|
| 94 | if(result1) sLog.outErrorDb("ERROR: Creature (guidlow %d, entry %d) have invalid coordinates in his waypoint %d (X: %d, Y: %d).", id, result1->Fetch()[0].GetUInt32(), point, node.x, node.y); |
---|
| 95 | else sLog.outErrorDb("ERROR: Waypoint path %d, have invalid coordinates in his waypoint %d (X: %d, Y: %d).", id, point, node.x, node.y); |
---|
| 96 | |
---|
[44] | 97 | Trinity::NormalizeMapCoord(node.x); |
---|
| 98 | Trinity::NormalizeMapCoord(node.y); |
---|
[2] | 99 | if(result1) |
---|
| 100 | { |
---|
| 101 | node.z = MapManager::Instance ().GetBaseMap(result1->Fetch()[1].GetUInt32())->GetHeight(node.x, node.y, node.z); |
---|
| 102 | delete result1; |
---|
| 103 | } |
---|
| 104 | WorldDatabase.PExecute("UPDATE creature_movement SET position_x = '%f', position_y = '%f', position_z = '%f' WHERE id = '%u' AND point = '%u'", node.x, node.y, node.z, id, point); |
---|
| 105 | } |
---|
| 106 | |
---|
| 107 | WaypointBehavior be; |
---|
| 108 | be.model1 = fields[4].GetUInt32(); |
---|
| 109 | be.model2 = fields[5].GetUInt32(); |
---|
| 110 | be.emote = fields[7].GetUInt32(); |
---|
| 111 | be.spell = fields[8].GetUInt32(); |
---|
| 112 | be.text[0] = fields[9].GetCppString(); |
---|
| 113 | be.text[1] = fields[10].GetCppString(); |
---|
| 114 | be.text[2] = fields[11].GetCppString(); |
---|
| 115 | be.text[3] = fields[12].GetCppString(); |
---|
| 116 | be.text[4] = fields[13].GetCppString(); |
---|
| 117 | |
---|
| 118 | // save memory by not storing empty behaviors |
---|
| 119 | if(!be.isEmpty()) |
---|
| 120 | { |
---|
| 121 | node.behavior = new WaypointBehavior(be); |
---|
| 122 | ++total_behaviors; |
---|
| 123 | } |
---|
| 124 | else |
---|
| 125 | node.behavior = NULL; |
---|
| 126 | bar.step(); |
---|
| 127 | } while( result->NextRow() ); |
---|
| 128 | delete result; |
---|
| 129 | } |
---|
| 130 | sLog.outString( ">> Loaded %u paths, %u nodes and %u behaviors", total_paths, total_nodes, total_behaviors); |
---|
| 131 | } |
---|
| 132 | |
---|
| 133 | void WaypointManager::Cleanup() |
---|
| 134 | { |
---|
| 135 | // check if points need to be renumbered and do it |
---|
| 136 | if(QueryResult *result = WorldDatabase.Query("SELECT 1 from creature_movement As T WHERE point <> (SELECT COUNT(*) FROM creature_movement WHERE id = T.id AND point <= T.point) LIMIT 1")) |
---|
| 137 | { |
---|
| 138 | delete result; |
---|
| 139 | WorldDatabase.DirectExecute("CREATE TEMPORARY TABLE temp LIKE creature_movement"); |
---|
| 140 | WorldDatabase.DirectExecute("INSERT INTO temp SELECT * FROM creature_movement"); |
---|
| 141 | WorldDatabase.DirectExecute("ALTER TABLE creature_movement DROP PRIMARY KEY"); |
---|
| 142 | WorldDatabase.DirectExecute("UPDATE creature_movement AS T SET point = (SELECT COUNT(*) FROM temp WHERE id = T.id AND point <= T.point)"); |
---|
| 143 | WorldDatabase.DirectExecute("ALTER TABLE creature_movement ADD PRIMARY KEY (id, point)"); |
---|
| 144 | WorldDatabase.DirectExecute("DROP TABLE temp"); |
---|
| 145 | assert(!(result = WorldDatabase.Query("SELECT 1 from creature_movement As T WHERE point <> (SELECT COUNT(*) FROM creature_movement WHERE id = T.id AND point <= T.point) LIMIT 1"))); |
---|
| 146 | } |
---|
| 147 | } |
---|
| 148 | |
---|
| 149 | void WaypointManager::Unload() |
---|
| 150 | { |
---|
| 151 | for(WaypointPathMap::iterator itr = m_pathMap.begin(); itr != m_pathMap.end(); ++itr) |
---|
| 152 | _clearPath(itr->second); |
---|
| 153 | m_pathMap.clear(); |
---|
| 154 | } |
---|
| 155 | |
---|
| 156 | void WaypointManager::_clearPath(WaypointPath &path) |
---|
| 157 | { |
---|
| 158 | for(WaypointPath::iterator itr = path.begin(); itr != path.end(); ++itr) |
---|
| 159 | if(itr->behavior) |
---|
| 160 | delete itr->behavior; |
---|
| 161 | path.clear(); |
---|
| 162 | } |
---|
| 163 | |
---|
| 164 | /// - Insert after the last point |
---|
| 165 | void WaypointManager::AddLastNode(uint32 id, float x, float y, float z, float o, uint32 delay, uint32 wpGuid) |
---|
| 166 | { |
---|
| 167 | _addNode(id, GetLastPoint(id, 0) + 1, x, y, z, o, delay, wpGuid); |
---|
| 168 | } |
---|
| 169 | |
---|
| 170 | /// - Insert after a certain point |
---|
| 171 | void WaypointManager::AddAfterNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid) |
---|
| 172 | { |
---|
| 173 | for(uint32 i = GetLastPoint(id, 0); i > point; i--) |
---|
| 174 | WorldDatabase.PExecuteLog("UPDATE creature_movement SET point=point+1 WHERE id='%u' AND point='%u'", id, i); |
---|
| 175 | |
---|
| 176 | _addNode(id, point + 1, x, y, z, o, delay, wpGuid); |
---|
| 177 | } |
---|
| 178 | |
---|
| 179 | /// - Insert without checking for collision |
---|
| 180 | void WaypointManager::_addNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid) |
---|
| 181 | { |
---|
| 182 | if(point == 0) return; // counted from 1 in the DB |
---|
| 183 | WorldDatabase.PExecuteLog("INSERT INTO creature_movement (id,point,position_x,position_y,position_z,orientation,wpguid,waittime) VALUES ('%u','%u','%f', '%f', '%f', '%f', '%d', '%d')", id, point, x, y, z, o, wpGuid, delay); |
---|
| 184 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
| 185 | if(itr == m_pathMap.end()) |
---|
| 186 | itr = m_pathMap.insert(WaypointPathMap::value_type(id, WaypointPath())).first; |
---|
| 187 | itr->second.insert(itr->second.begin() + (point - 1), WaypointNode(x, y, z, o, delay, NULL)); |
---|
| 188 | } |
---|
| 189 | |
---|
| 190 | uint32 WaypointManager::GetLastPoint(uint32 id, uint32 default_notfound) |
---|
| 191 | { |
---|
| 192 | uint32 point = default_notfound; |
---|
| 193 | /*QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM creature_movement WHERE id = '%u'", id); |
---|
| 194 | if( result ) |
---|
| 195 | { |
---|
| 196 | point = (*result)[0].GetUInt32()+1; |
---|
| 197 | delete result; |
---|
| 198 | }*/ |
---|
| 199 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
| 200 | if(itr != m_pathMap.end() && itr->second.size() != 0) |
---|
| 201 | point = itr->second.size(); |
---|
| 202 | return point; |
---|
| 203 | } |
---|
| 204 | |
---|
| 205 | void WaypointManager::DeleteNode(uint32 id, uint32 point) |
---|
| 206 | { |
---|
| 207 | if(point == 0) return; // counted from 1 in the DB |
---|
| 208 | WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id='%u' AND point='%u'", id, point); |
---|
| 209 | WorldDatabase.PExecuteLog("UPDATE creature_movement SET point=point-1 WHERE id='%u' AND point>'%u'", id, point); |
---|
| 210 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
| 211 | if(itr != m_pathMap.end() && point <= itr->second.size()) |
---|
| 212 | itr->second.erase(itr->second.begin() + (point-1)); |
---|
| 213 | } |
---|
| 214 | |
---|
| 215 | void WaypointManager::DeletePath(uint32 id) |
---|
| 216 | { |
---|
| 217 | WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id='%u'", id); |
---|
| 218 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
| 219 | if(itr != m_pathMap.end()) |
---|
| 220 | _clearPath(itr->second); |
---|
| 221 | // the path is not removed from the map, just cleared |
---|
| 222 | // WMGs have pointers to the path, so deleting them would crash |
---|
| 223 | // this wastes some memory, but these functions are |
---|
| 224 | // only meant to be called by GM commands |
---|
| 225 | } |
---|
| 226 | |
---|
| 227 | void WaypointManager::SetNodePosition(uint32 id, uint32 point, float x, float y, float z) |
---|
| 228 | { |
---|
| 229 | if(point == 0) return; // counted from 1 in the DB |
---|
| 230 | WorldDatabase.PExecuteLog("UPDATE creature_movement SET position_x = '%f',position_y = '%f',position_z = '%f' where id = '%u' AND point='%u'", x, y, z, id, point); |
---|
| 231 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
| 232 | if(itr != m_pathMap.end() && point <= itr->second.size()) |
---|
| 233 | { |
---|
| 234 | itr->second[point-1].x = x; |
---|
| 235 | itr->second[point-1].y = y; |
---|
| 236 | itr->second[point-1].z = z; |
---|
| 237 | } |
---|
| 238 | } |
---|
| 239 | |
---|
| 240 | void WaypointManager::SetNodeText(uint32 id, uint32 point, const char *text_field, const char *text) |
---|
| 241 | { |
---|
| 242 | if(point == 0) return; // counted from 1 in the DB |
---|
| 243 | if(!text_field) return; |
---|
| 244 | std::string field = text_field; |
---|
| 245 | WorldDatabase.escape_string(field); |
---|
| 246 | |
---|
| 247 | if(!text) |
---|
| 248 | { |
---|
| 249 | WorldDatabase.PExecuteLog("UPDATE creature_movement SET %s=NULL WHERE id='%u' AND point='%u'", field.c_str(), id, point); |
---|
| 250 | } |
---|
| 251 | else |
---|
| 252 | { |
---|
| 253 | std::string text2 = text; |
---|
| 254 | WorldDatabase.escape_string(text2); |
---|
| 255 | WorldDatabase.PExecuteLog("UPDATE creature_movement SET %s='%s' WHERE id='%u' AND point='%u'", field.c_str(), text2.c_str(), id, point); |
---|
| 256 | } |
---|
| 257 | |
---|
| 258 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
| 259 | if(itr != m_pathMap.end() && point <= itr->second.size()) |
---|
| 260 | { |
---|
| 261 | WaypointNode &node = itr->second[point-1]; |
---|
| 262 | if(!node.behavior) node.behavior = new WaypointBehavior(); |
---|
| 263 | |
---|
| 264 | if(field == "text1") node.behavior->text[0] = text ? text : ""; |
---|
| 265 | if(field == "text2") node.behavior->text[1] = text ? text : ""; |
---|
| 266 | if(field == "text3") node.behavior->text[2] = text ? text : ""; |
---|
| 267 | if(field == "text4") node.behavior->text[3] = text ? text : ""; |
---|
| 268 | if(field == "text5") node.behavior->text[4] = text ? text : ""; |
---|
| 269 | if(field == "emote") node.behavior->emote = text ? atoi(text) : 0; |
---|
| 270 | if(field == "spell") node.behavior->spell = text ? atoi(text) : 0; |
---|
| 271 | if(field == "model1") node.behavior->model1 = text ? atoi(text) : 0; |
---|
| 272 | if(field == "model2") node.behavior->model2 = text ? atoi(text) : 0; |
---|
| 273 | } |
---|
| 274 | } |
---|